home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Overload Trio 2
/
Shareware Overload Trio Volume 2 (Chestnut CD-ROM).ISO
/
dir34
/
2m21src.zip
/
2MX.ASM
< prev
Wrap
Assembly Source File
|
1994-05-31
|
129KB
|
3,059 lines
;┌───────────────────────────────────────────────────────────────────┐
;│ │
;│ █████ █ █ █ █ │
;│ █ ██ ██ █ █ ▄▄▄▄▄ ▄ │
;│ █████ █ █ █ █ █ ▄▀█ │
;│ █ █ █ █ █ █▀▀▀▀ █ │
;│ █████ █ █ █ █ █▄▄▄▄ ▄ ▄▄█▄▄ │
;│ │
;│ 2MX 2.1 - (C) Mayo 1994 Ciriaco García de Celis. │
;│ │
;│ SOPORTE PARA DISQUETES CON MAYOR CAPACIDAD DE LA NORMAL │
;│ CREADOS POR LA UTILIDAD 2MF │
;│ │
;│ - Sólo para XT con controladora y unidades de alta. │
;│ - Programación directa del controlador de disquetes y del DMA. │
;│ - Soporte para sectores de más de 512 bytes (economizar GAPs). │
;│ - Mezcla de sectores de distinto tamaño en la misma pista. │
;│ - Ruptura del límite habitual de 80 pistas (incluido 360K). │
;│ - Emulación de sectores de 512 bytes en INT 13h. │
;│ - Función 5 (INT 13h) reforzada para formatear los nuevos discos. │
;│ - El soporte residente opera eficazmente bajo DOS y WINDOWS 3.0 │
;│ │
;│ Emplear TASM /m5 y TLINK /t para obtener un fichero COM │
;│ │
;└───────────────────────────────────────────────────────────────────┘
; ------------ Macros de propósito general.
XPUSH MACRO regmem ; apilar lista de registros
IRP rm, <regmem>
PUSH rm
ENDM
ENDM
XPOP MACRO regmem ; desapilar lista de registros
IRP rm, <regmem>
POP rm
ENDM
ENDM
XPUSHA MACRO
XPUSH <AX, BX, CX, DX, SI, DI>
ENDM
XPOPA MACRO
XPOP <DI, SI, DX, CX, BX, AX>
ENDM
XSHL MACRO regmem, cuenta
REPT cuenta
SHL regmem,1
ENDM
ENDM
DELAY MACRO ; sin estados de espera
ENDM
; ------------ Estructura de datos con información para cada unidad.
info_drv STRUC
maxs EQU 13 ; máximo 13 sectores físicos/pista
tipo_drv DB ? ; tipo de la disquetera (0 = no hay)
control2m_flag DB OFF ; a ON si 2M controla la unidad
cambio DB ON ; a ON indica cambio de soporte
version_fmt DB ? ; versión del formato de disco 2M
multi_io DB ? ; a 0 si posible acceso multi-sector
chk DB ? ; a 0 si checksum del sector 0 Ok
vunidad EQU THIS WORD
vunidad0 DB ? ; velocidad pista 0
vunidadx DB ? ; velocidad demás pistas
gap DB ? ; GAP entre sectores (leer/escribir)
sectpista DB ? ; sectores lógicos por pista
tabla_tsect DB maxs DUP (?) ; tamaños de sectores 1, 2, ..., N
tam_fat DB ? ; sectores/FAT en la unidad
ENDS
; ------------ Programa.
_PRINCIPAL SEGMENT
ASSUME CS:_PRINCIPAL, DS:_PRINCIPAL
ORG 100h
ini_residente EQU $
; ****************************************
; * *
; * D A T O S R E S I D E N T E S *
; * *
; ****************************************
inicio: JMP main
; ------------ Identificación estandarizada del programa.
program_id LABEL BYTE
segmento_real DW 0 ; segmento real donde será cargado
offset_real DW 0 ; offset real " " "
longitud_total DW 0 ; zona de memoria ocupada (párrafos)
info_extra DB 80h ; bits 0, 1 y 2-> 000: normal, con PSP
; 001: bloque UMB XMS
; 010: *.SYS
; 011: *.SYS formato EXE
; bit 7 a 1: «extension_id» definida
multiplex_id DB 0 ; número Multiplex de este TSR
vectores_id DW tabla_vectores
extension_id DW tabla_extra
DB "*##*"
autor_nom_ver DB "CiriSOFT:2MX:2.1",0
DB 2 ; número de vectores de interrupción usados
tabla_vectores EQU $
vieja_i13 DB 13h ; INT 13h
ant_int13 LABEL DWORD ; dirección original
ant_int13_off DW 0
ant_int13_seg DW 0
DB 2Fh ; INT 2Fh
ant_int2F LABEL DWORD ; dirección original
ant_int2F_off DW 0
ant_int2F_seg DW 0
tabla_extra LABEL BYTE
DW ctrl_exterior ; permitido control exterior
DW 0 ; campo reservado
ctrl_exterior LABEL BYTE
reubicabilidad DB 1 ; programa 100% reubicable
activacion DW act
act DB 1
; ------------ Variables del programa.
info_ptr DW info_A ; punteros a datos de las unidades
DW info_B
id_sistema DB "2M-STV" ; identificación de disco 2M
tbase DW ? ; base de tiempos para retardos
unidad DB ? ; unidad física de disco en curso
numsect DW ? ; sectores a transferir
sectini DW ? ; primer sector DOS a transferir
cilindro DB ? ; cilindro del disco a acceder
cabezal DB ? ; cabezal a emplear
sector DB ? ; número de sector físico
sector_ini DB ? ; número de sector físico inicial
sector_fin DB ? ; número de sector físico final
seccion DB ? ; parte del sector físico en curso
secciones DB ? ; sectores lógicos a transferir
tsector DB ? ; LOG2 (tamaño de sector) - 7
buffer DW buffer_io ; puntero al buffer intermedio
buf_unidad DB ? ; unidad del sector en el buffer
buf_cilcab DW ? ; cilindro/cabezal de sector buffer
buf_sector DB ? ; número de sector en el buffer
status DB ? ; resultado de los accesos a disco
fdc_result DB 7 DUP (?) ; bytes de resultados del FDC
orden DB ? ; operación F_READ/F_WRITE/F_VERIFY
tab_ordenes DB F_READ
DB F_WRITE
DB F_VERIFY ; órdenes 2, 3 y 4
; --- Interpretación BIOS de los bits de ST1
lista_errs DB 4 ; 'sector not found'
DB 0
DB 10h ; 'bad CRC'
DB 8 ; 'DMA overrun'
DB 0
DB 4 ; 'sector not found'
DB 3 ; 'write-protect error'
DB 2 ; 'address mark not found'
DB 20h ; en otro caso: 'bad NEC'
info_A info_drv <> ; datos de A:
info_B info_drv <> ; datos de B:
; ***************************************
; * *
; * C O D I G O R E S I D E N T E *
; * *
; ***************************************
; ------------ Rutina de gestión de INT 2Fh.
ges_int2F PROC FAR
STI
CMP AH,CS:multiplex_id
JE preguntan
JMP CS:ant_int2F ; saltar al gestor de INT 2Fh
preguntan: CMP DI,1992h
JNE ret_no_info ; no llama alguien del convenio
MOV AX,ES
CMP AX,1492h
JNE ret_no_info ; no llama alguien del convenio
PUSH CS
POP ES ; sí llama: darle información
LEA DI,autor_nom_ver
ret_no_info: MOV AX,0FFFFh ; "entrada multiplex en uso"
IRET
ges_int2F ENDP
; ------------ Nueva rutina de gestión de INT 13h. Llama a la INT 13h
; original o a una nueva rutina de control para la
; lectura (AH=2), escritura (AH=3) y verificación (AH=4)
; según el tipo de disco introducido. Ante una función de
; formateo (AH=5) se entrega el control a la INT 13h
; original. Se detecta un posible cambio de disco y se
; retorna en ese caso con el correspondiente error.
ges_int13 PROC FAR
STI
CLD
PUSHF
CMP DL,2
JAE ges13bios ; no es disquetera A: ó B:
PUSH SI
CALL set_SI_drv
CMP CS:[SI].tipo_drv,2 ; ¿unidad 1.2M?
JE ges_2m
CMP CS:[SI].tipo_drv,4 ; ¿unidad 1.44/2.88M?
ges_2m: POP SI
JC ges13bios ; no es unidad de alta densidad
CMP AH,2
JB ges13bios ; no Read/Write/Verify/Format
CMP AH,5
JA ges13bios ; no Read/Write/Verify/Format
CALL detecta_cambio ; ¿cambio de disco?
JNC sin_cambio
POPF
STC ; hubo cambio:
MOV AX,600h
RET 2 ; retornar con error
sin_cambio: CMP AH,5
JNE dilucida ; no es orden de formateo
CALL leer_lin_camb
JNZ format_bios ; no hay disquete en la unidad
CMP AL,7Fh
JNE format_bios ; no es orden formateo de 2M
CMP SI,"2M"
JE format_2m ; es orden de formateo de 2M
format_bios: CLC
CALL set_flag_STV ; CF = 0 -> indicar no 2M
dilucida: PUSH SI
CALL set_SI_drv ; SI -> variables de la unidad
CMP CS:[SI].control2m_flag,OFF
POP SI
JE ges13bios ; la unidad la controla la BIOS
POPF
CALL control2m ; la controla 2M
RET 2
ges13bios: POPF
JMP CS:ant_int13 ; saltar al gestor de INT 13h
; --- Función de formateo implementada por 2M. En los
; disquetes creados con /M todas las pistas salvo
; la 0 deberían ser formateadas invocando INT 13h
; de manera directa (con CALL) para evitar que se
; ejecute cierto código de WINDOWS en el modo
; protegido que provoca errores al formatear. Antes
; de formatear la primera pista física del disco se
; invoca la función de formateo de la INT 13h
; original (con un error para provocar un rápido
; retorno) con objeto de informar al DOS y a todos
; los TSR previos del cambio de soporte.
format_2m: POPF
PUSH DS ; *
XPUSHA ; **
PUSH CS
POP DS
MOV unidad,DL
CALL set_SI_drv
MOV cilindro,CH
MOV cabezal,DH
OR CH,DH
JNZ format_trx ; no es cilindro 0 y cabezal 0
XPUSHA
MOV AL,1 ; formatear un sector (AH=5)
MOV CH,0
MOV DH,2 ; en cabezal 2 (incorrecto)
PUSHF
CALL ant_int13 ; avisar al DOS del nuevo disco
CLD ; mantener DF=0
STC
CALL reset_drv ; asegurar aceleración motor
XPOPA
CALL set_info ; características nuevo soporte
format_trx: CALL genera_info ; tabla de información formateo
CALL motor_ok ; asegurar que está en marcha
CALL seek_drv
CALL formatea_pista
PUSHF
CLC
CALL motor_off_cnt ; cuenta normal detención motor
POPF
CALL set_err
CALL set_bios_err ; no altera flags
XPOPA ; **
MOV AH,status
POP DS ; *
RET 2
ges_int13 ENDP
; ------------ A la entrada en DL se indica la unidad y a la salida se
; devuelve SI apuntando sus variables sin alterar flags.
set_SI_drv PROC
PUSHF
PUSH BX
MOV BL,DL
MOV BH,0
SHL BX,1
MOV SI,CS:[BX+OFFSET info_ptr]
POP BX
POPF
RET
set_SI_drv ENDP
; ------------ Si CF=1, indicar disquete 2M presente. A la
; entrada, DL indica la unidad de disco.
set_flag_STV PROC
XPUSHA
CALL set_SI_drv
MOV AL,ON ; indicar 2M
JC tipo_stv_ok
MOV AL,OFF ; indicar no 2M
tipo_stv_ok: MOV CS:[SI].control2m_flag,AL
XPOPA
RET
set_flag_STV ENDP
; ------------ Devolver ZF=1 si cilindro y cabezal 0.
pista0? PROC
PUSH AX
MOV AL,cabezal
OR AL,cilindro
POP AX
RET
pista0? ENDP
; ------------ Devolver ZF=1 si la línea de cambio de disco está
; inactiva. A la entrada, DL contiene la unidad. El
; motor es puesto en marcha y, si no lo estaba ya, la
; variable que indica lo que resta para detenerlo
; es llevada a su valor normal, por lo que el disco no
; tardará mucho en detenerse (incluso sin quizá haber
; acelerado aún). En la práctica, invocando esta rutina
; desde INT 13h nunca será necesario arrancar el motor
; ya que el DOS ejecuta antes la función equivalente,
; la 16h, que lo pone en marcha. Es simplemente una
; medida de seguridad contra las BIOS «de marca».
leer_lin_camb PROC
XPUSHA ; *
PUSH DS
MOV CX,40h
MOV DS,CX
MOV AL,1
MOV CL,DL
SHL AL,CL ; bit de motor en 0..3
TEST DS:[3Fh],AL
JNZ rodando ; el motor ya está girando
CLC
CALL motor_off_cnt ; cuenta normal detención motor
rodando: MOV AH,DL
XSHL AH,4
OR AH,AL ; AH = byte BIOS
XSHL AL,4
OR AL,00001100b ; modo DMA, no hacer reset
OR AL,DL ; AL para reg. salida digital
MOV DX,3F2h
CLI
MOV DS:[3Fh],AH ; actualizar variable BIOS
OUT DX,AL ; arrancado motor en la unidad
ADD DX,5
DELAY
IN AL,DX ; leer línea de cambio de disco
STI
TEST AL,80h ; ZF=0 -> cambio de disco
POP DS
XPOPA ; *
RET
leer_lin_camb ENDP
; ------------ Determinar si ha habido cambio de disco y, en ese caso,
; si el nuevo disquete es de tipo 2M o no. El cambio de
; disco se detecta leyendo la línea de cambio de disco o
; chequeando la variable que indica si ha habido cambio
; o no (esta variable está a ON tras instalar 2M para
; forzar la detección del tipo de disco introducido; se
; pone en ON también si no se logra bajar la línea de
; cambio de disco por si fuera un soporte raro y la BIOS
; sí lo lograra -forzando así una detección posterior-).
detecta_cambio PROC
XPUSHA ; *
CALL set_SI_drv ; SI -> variables de la unidad
CMP CS:[SI].cambio,ON ; ¿cambio de soporte?
MOV CS:[SI].cambio,OFF
JE hubo_cambio
CALL leer_lin_camb ; leer línea de cambio de disco
JNZ hubo_cambio
XPOPA
CLC ; no hay cambio de disco
RET
hubo_cambio: CLC
CALL set_flag_STV ; CF = 0 -> supuesto no 2M
XPUSH <DS, ES> ; **
MOV BX,90h
ADD BL,DL
MOV CX,40h
MOV DS,CX
AND BYTE PTR [BX],255-16 ; densidad no determinada
XPUSH <CS, CS>
XPOP <DS, ES>
MOV unidad,DL
STC ; asegurar motor en marcha
CALL reset_drv
MOV cilindro,1
MOV cabezal,0
CALL seek_drv ; bajar línea cambio de disco
DEC cilindro
CALL seek_drv
CLC
CALL motor_off_cnt ; cuenta normal detención motor
CALL leer_lin_camb ; ¿bajada línea cambio disco?
JZ disco_dentro ; se pudo: hay disco dentro
MOV [SI].cambio,ON ; futura detección tipo disco
CLC ; NO indicar cambio de disco...
JMP fin_detecta ; ...para pasar control a BIOS
disco_dentro: PUSH DS
MOV CX,40h
MOV DS,CX
MOV BYTE PTR DS:[41h],6 ; error 'media changed'
POP DS
CMP AH,5 ; ¿función de formateo?
JE fin_detecta_c ; no perder el tiempo
MOV buf_unidad,-1 ; invalidar buffer
MOV [SI].gap,20 ; GAP provisional
MOV CX,3 ; 3 intentos
intenta_io0: PUSH CX
CMP CX,2 ; CF=1 la 3ª vez (a 0 si CX<>1)
CALL reset_drv
MOV [SI].vunidad0,0 ; empezar con 500 Kbit/seg.
intenta_io: MOV cilindro,0
MOV cabezal,0
MOV sector,1 ; sector de arranque
MOV seccion,0
MOV secciones,1
MOV orden,F_READ
MOV DI,buffer
CALL direct_acceso
JNE otra_densidad ; es otra densidad de disco
POP CX
MOV BX,buffer
CALL set_info ; características nuevo soporte
CLC
JMP fin_detecta_c ; indicar cambio de disco
otra_densidad: MOV AL,[SI].vunidad0
INC AX ; próxima velocidad
CMP AL,3
JA otro_intento
MOV [SI].vunidad0,AL
JMP intenta_io ; probar otra velocidad
otro_intento: MOV [SI].vunidad0,0
POP CX
LOOP intenta_io0 ; reintento
fin_detecta_c: STC ; indicar cambio de disco
fin_detecta: XPOP <ES, DS> ; **
XPOPA ; *
RET
detecta_cambio ENDP
; ------------ Anotar la información del disquete si es de tipo 2M.
; A la entrada, DS:SI apunta a las variables de la unidad
; y ES:BX al sector de arranque del disco. Se actualiza
; también la variable BIOS de tipo de densidad (la BIOS
; no se da cuenta del cambio de disco y conviene ayudar).
set_info PROC
XPUSHA
CALL calc_chk
JC set_info_exit ; no es disco 2M
MOV [SI].chk,AL ; anotar checksum
MOV [SI].version_fmt,CL ; y versión del formato
MOV DL,unidad
STC
CALL set_flag_STV ; CF = 1 -> indicar disco 2M
MOV AL,ES:[BX+22] ; tamaño de FAT
MOV [SI].tam_fat,AL
MOV CL,ES:[BX+65] ; CL a 0 si acceso multi-sector
MOV [SI].multi_io,CL
MOV AX,ES:[BX+66]
MOV [SI].vunidad,AX ; velocidad pista 0 / demás
MOV AL,ES:[BX+24]
MOV [SI].sectpista,AL ; sectores/pista
MOV DI,ES:[BX+72]
MOV AL,ES:[BX+DI+1] ; GAP de formateo
MOV AH,AL
AND CL,CL ; CL a 0 si acceso multi-sector
JZ gap_rw_ok ; GAP R/W para /F
ADD AH,190
MOV AL,11
MUL AH ; AX = (190+GAP)*11
SUB AX,2048+62
gap_rw_ok: SHR AL,1 ; GAP R/W para /M
MOV [SI].gap,AL
MOV CX,maxs
MOV DI,ES:[BX+74]
ADD DI,BX
LEA BX,[SI].tabla_tsect
genera_ts: MOV AL,ES:[DI]
MOV [BX],AL
INC BX
INC DI
LOOP genera_ts ; información estructura pistas
set_info_exit: MOV AL,[SI].vunidad0
MOV CL,6
SHL AL,CL ; velocidad en bits 7:6
OR AL,00010111b ; establecido otro medio físico
CMP [SI].tipo_drv,2
JA modo_ok ; es unidad de 3½
AND AL,11111000b
OR AL,00000101b ; 1.2 en 1.2
TEST AL,01000000b
JZ modo_ok
XOR AL,00100001b ; 360 en 1.2 y seek * 2
modo_ok: PUSH DS
MOV BX,90h
ADD BL,unidad
PUSH 40h
POP DS
AND BYTE PTR DS:[BX],8 ; respetar bit de 2.88M
OR DS:[BX],AL ; actualizar variable BIOS
POP DS ; con el tipo de densidad
XPOPA
RET
set_info ENDP
; ------------ Calcular el checksum de la zona vital del sector de
; arranque. A la entrada, ES:BX -> sector de arranque.
; A la salida, CF=1 si el disco no es 2M; de otro modo
; checksum en AL y versión del formato de disco en CL.
calc_chk PROC
XPUSH <SI, DI>
LEA DI,[BX+3] ; DI=BX+3
LEA SI,id_sistema
MOV CX,6
REP CMPSB ; comparar identificación
STC
JNE chk_ret ; el disco no es 2M
XOR AX,AX
MOV CL,ES:[BX+64] ; versión del formateador
CMP CL,6
JB chk_ok ; no usaba este checksum
MOV DI,ES:[BX+68]
chk_sum: DEC DI
ADD AL,ES:[BX+DI]
CMP DI,63
JA chk_sum
chk_ok: CLC
chk_ret: XPOP <DI, SI>
RET
calc_chk ENDP
; ------------ Determinar el tipo de error producido en el acceso.
set_err PROC
XPUSH <AX, BX, CX>
JNC err_ret ; no hay error
CMP status,0 ; ¿'status' ya asignado?
JNE err_retc ; no cambiarlo si es así
MOV AL,BYTE PTR fdc_result+1
AND AL,10110111b ; aislar condiciones de test
LEA BX,lista_errs
MOV CX,9
busca_err: MOV AH,[BX] ; código de error BIOS
SHL AL,1
JC err_ok ; es ese error
INC BX
LOOP busca_err ; buscar otro error
err_ok: OR status,AH
err_retc: STC ; condición de error
err_ret: XPOP <CX, BX, AX>
RET
set_err ENDP
; ------------ Actualizar variables de error de la BIOS.
set_bios_err PROC
PUSHF ; *
XPUSHA ; **
PUSH ES ; ***
MOV CX,40h
MOV ES,CX
MOV DI,41h ; bytes de resultados del 765
LEA SI,status ; variable BIOS de status y 7
MOV CX,4 ; bytes: 4 palabras
REP MOVSW
POP ES ; ***
XPOPA ; **
POPF ; *
RET
set_bios_err ENDP
; ------------ Realizar lecturas, escrituras y verificaciones: rutina
; que sustituye el código de la BIOS para poder soportar
; los formatos 2M. La operación puede quedar dividida en
; tres fases: el fragmento anterior a la FAT2, la zona
; correspondiente a la FAT2 (se ignora la escritura y se
; simula su lectura leyendo la FAT1) y un último bloque
; ubicado tras la FAT2. El sector de arranque es emulado
; empleando el primer sector físico de la FAT2 (aunque en
; los discos de versión de formato anterior a la 7 se usa
; el sector de arranque verdadero -permitiendo escribirlo
; sólo si es válido-). En cualquier caso, si el número de
; cabezal tiene el bit 7 activo, se sobreentiende que el
; programa que llama soporta disquetes 2M y no se emula
; la FAT2 ni el sector de arranque, para permitirle
; acceder al código SuperBOOT. Las coordenadas de la BIOS
; se traducen a las unidades del DOS por mayor comodidad.
control2m PROC
PUSH DS ; *
XPUSHA ; **
PUSH CS
POP DS
MOV unidad,DL
CALL set_SI_drv ; SI -> variables de la unidad
CMP [SI].chk,0
JE chk_valido ; checksum correcto en sector 0
MOV status,40h ; devolver 'Seek Error' al DOS
JMP exit_2m_ctrl
chk_valido: PUSH AX ; ***
MOV AH,0
MOV numsect,AX ; nº sectores
MOV AL,CH ; cilindro
SHL AL,1
MOV DL,DH
AND DH,01111111b
ADD AL,DH ; cabezal físico
MUL [SI].sectpista
ADD AL,CL ; sector
ADC AH,0
DEC AX ; AX = nº sector DOS
MOV sectini,AX
MOV DI,BX ; ES:DI -> dirección
POP BX ; ***
MOV BL,BH
MOV BH,0
MOV CL,[BX+OFFSET tab_ordenes-2]
MOV orden,CL
SHL DL,1
JC acceso_final ; cabezal >= 128: no emular
AND AX,AX ; ¿comienza en sector 0?
JNZ io_emula ; no
CMP [SI].version_fmt,7
JB boot_real ; no soportado BOOT virtual
MOV AL,[SI].tam_fat ; AH = 0
INC AX
MOV CX,1 ; sector BOOT emulado en
CALL ejecuta_io ; el primer sector FAT2
JNE fin_ctrl
boot_fin_op: DEC numsect
INC sectini
MOV AX,sectini
JMP io_emula
boot_real: CMP orden,F_WRITE
JNE io_emula
MOV BX,DI ; BOOT de 2M 1.3 y anteriores
CALL calc_chk
JC si_skip ; no es de tipo 2M
AND AL,AL
JZ io_emula ; lo es y con checksum correcto
si_skip: ADD DI,512
JMP boot_fin_op ; impedir estropicio de BOOT
io_emula: MOV CL,[SI].tam_fat
MOV CH,0 ; CX = primer sector FAT2 - 1
CMP AX,CX
JA en_fat2? ; ¿la operación afecta a FAT2?
CALL calc_iop ; calcular sectores antes FAT2
CALL ejecuta_io ; CX sectores desde AX
JNE fin_ctrl ; error
CMP numsect,0
JE fin_ctrl ; fin de la transferencia
en_fat2?: MOV AX,sectini
MOV CL,[SI].tam_fat
MOV CH,0
SHL CX,1 ; CX = último sector FAT2
CMP AX,CX
JA acceso_final ; la operación es tras la FAT2
CALL calc_iop ; sectores hasta fin de FAT2
CMP orden,F_WRITE
JNE emula_fat1
XCHG CH,CL
SHL CH,1 ; CX = CX * 512
ADD DI,CX ; ES:DI actualizado
JMP acceso_final
emula_fat1: MOV DL,[SI].tam_fat
MOV DH,0
SUB AX,DX ; leer de FAT1 y no de la FAT2
CALL ejecuta_io ; CX sectores desde AX
JNE fin_ctrl ; error
acceso_final: CMP numsect,0
JE fin_ctrl ; fin de la transferencia
MOV AX,sectini
MOV CX,numsect
CALL ejecuta_io
fin_ctrl: CLC
CALL motor_off_cnt ; cuenta normal detención motor
CALL set_bios_err ; actualizar variables BIOS
exit_2m_ctrl: XPOPA ; **
MOV AH,status
POP DS ; *
AND AH,AH
JZ st_ok ; resultado correcto (CF=0)
STC ; error
MOV AL,0 ; 0 sectores movidos
st_ok: RET
calc_iop: SUB CX,AX
INC CX ; CX sectores
CMP CX,numsect
JBE nsect_ok
MOV CX,numsect ; sólo quedan CX
nsect_ok: SUB numsect,CX
ADD sectini,CX
RET
control2m ENDP
; ------------ A la entrada, AX indica el sector inicial (coordenadas
; del DOS) y CX el número de sectores a procesar.
; * Definiciones: «Sector físico» es un sector del disco
; de 512, 1024 ó 2048 bytes (números de sector del 1 al N
; en la pista). Este sector físico está dividido en
; «secciones» de 512 bytes, constando por tanto de 1, 2 ó
; 4 secciones. «Sector virtual» es el número de sector
; del programa que llama a INT 13h, comprendido entre 1 y
; M. Esta estructura de N sectores por pista de distintos
; tamaños, se verifica en todo el disco con excepción del
; cabezal y cilindro 0 (con un formato más convencional
; de sectores de 512 bytes numerados de 1 a J, aunque no
; existen algunos de los intermedios que corresponden a
; la segunda copia de la FAT).
; * Primero se convierte el sector virtual (1..M) en su
; correspondiente físico (1..J en la pista 0 y 1..N en
; las demás), deduciendo qué porción de 512 bytes (o
; sección) es afectada. Un sector virtual (512 bytes)
; simulado suele ser parte de un sector físico de 2048
; bytes en muchos casos. Si dicho sector físico ya había
; sido leído al buffer en anteriores accesos, se extrae
; la sección necesaria. Si no, se carga del disco y se
; extrae dicho fragmento. El número de sectores virtuales
; que se solicitan (=secciones) permite realizar un bucle
; hasta completar la transferencia; el interleave 1:2 de
; los sectores físicos en /M permite acceder sector a
; sector sin pérdida de rendimiento. En el caso de la
; escritura, se estudia primero si hay varios sectores
; virtuales consecutivos que escribir, completando entre
; todos un sector físico: en ese caso, se prepara el
; mismo y se escribe sin más. En caso de que haya que
; modificar sólo una única sección de un sector físico,
; salvo si éste es de 512 bytes, no hay más remedio que
; cargarlo al buffer (realizar una prelectura),
; actualizar la sección correspondiente y volverlo a
; escribir.
; * En el formato /F se realiza una operación multisector
; si es posible y sin emplear el buffer intermedio (si
; bien podría ser preciso emplearlo con la primera y
; última sección); en los dos formatos de disco se hace
; la operación multisector en la primera pista. Las
; operaciones multisector puede que sea preciso
; dividirlas en tres fases: los sectores antes de una
; frontera de DMA, el que la cruza (que es transferido
; a través del buffer intermedio) y los que están detrás.
ejecuta_io PROC
MOV BX,AX ; AX = sector DOS inicial
MOV secciones,CL ; CX sectores (CL realmente)
DIV [SI].sectpista
INC AH ; numerado desde 1...
MOV sector,AH ; ...el resto es el sector
SHR AL,1
MOV cilindro,AL ; cilindro
RCL AL,1
AND AL,1
MOV cabezal,AL ; cabezal
MOV AL,sector
ADD AL,secciones
JC no_cabe ; sector+secciones > 255
DEC AX ; DEC AX = DEC AL
CMP AL,[SI].sectpista
JBE si_cabe
no_cabe: MOV status,4 ; 'sector no encontrado'
JMP fin_io
si_cabe: MOV AL,AH ; sector en AL
CBW ; sección 0 (AH = 0)
CALL pista0?
JZ s_xx ; sector físico en pista/cara 0
LEA BX,[SI].tabla_tsect-1
DEC AX ; AH = 0
resta_secc: INC BX
INC AH
MOV CL,[BX]
SUB CL,2
MOV CH,1
SHL CH,CL
SUB AL,CH
JNC resta_secc ; en las demás pistas
ADD AL,CH
XCHG AH,AL
s_xx: MOV sector,AL ; sector lógico convertido a
MOV seccion,AH ; sector y sección físicas
direct_acceso: CALL motor_ok ; asegurar que está en marcha
MOV AH,0
MOV sector_fin,AH ; no acceder a más de 1 sector
CALL pista0? ; (al menos de momento)
JNZ decide_multi ; no es pista 0
MOV AL,secciones
MOV secciones,AH ; las que restan (AH = 0)
JMP multi_proc
decide_multi: CMP [SI].multi_io,AH ; AH = 0
JNE io_pasos ; acceso sector a sector
CMP seccion,AH
JE multi_acc
CALL acceso_secc ; no acceso a inicio sector
JC fin_io
multi_acc: CMP secciones,AH ; AH = 0
JE fin_io
CALL num_secciones
MOV CL,AL
MOV AL,secciones ; AH = 0
DIV CL
AND AL,AL
JZ io_pasos ; no quedan sectores enteros
MOV secciones,AH ; las que restan
multi_proc: CALL acceso_multi ; de AL sectores
JC fin_io
io_pasos: CMP secciones,0
JE fin_io ; no restan secciones finales
CALL acceso_secc
JNC io_pasos
fin_io: CMP status,0 ; ZF = 1 -> operación correcta
RET
acceso_secc: PUSH AX
CMP orden,F_WRITE ; acabar transferencia sector
JE escritura
CMP orden,F_VERIFY
JE verificacion
CALL leido? ; realizar lectura...
JNC ya_leido ; sector ya en el buffer
hay_que_leer: CALL acceso_sector ; efectuar E/S
JC acc_ret ; ha habido fallo
ya_leido: CALL trans_secc ; buffer -> memoria
JMP acc_ret
escritura: CMP seccion,0
JNE prelectura ; sólo parte del sector cambia
CALL num_secciones
CMP secciones,AL
JAE escribir ; Todo el sector físico cambia
prelectura: CALL leido? ; Leer el sector físico para
JNC escribir ; cambiar sólo una parte de él
MOV orden,F_READ ; de momento leer...
CALL acceso_sector ; efectuar E/S
MOV orden,F_WRITE ; ... restaurar orden original
JC acc_ret ; ha habido fallo
escribir: CALL trans_secc ; memoria -> buffer
CALL acceso_sector ; volcar buffer al disco
JMP acc_ret
verificacion: PUSH BX
MOV BL,seccion
CALL num_secciones
dec_sec_veri: DEC secciones
JZ verifica
INC BX
CMP BL,AL
JB dec_sec_veri
verifica: POP BX
CALL acceso_sector ; leer para forzar verificación
acc_ret: PUSHF
INC sector ; preparado para otro sector
MOV seccion,0 ; desde su primera sección
POPF
POP AX
RET
acceso_multi: PUSH AX ; AL = sectores a transferir
MOV BX,ES ; desde 'sector' teniendo
XSHL BX,4 ; cuidado con el DMA
ADD BX,DI
NEG BX ; BX = bytes hasta frontera DMA
CALL num_secciones
MOV CH,AL ; AL secciones de 512 bytes
MOV CL,0
SHL CX,1 ; CX = bytes por sector
XCHG AX,BX ; BL = secciones por sector
XOR DX,DX
DIV CX
MOV CL,AL ; CL = sectores que caben
POP AX ; AL = sectores a transferir
CMP AL,CL
JA acc_mult2 ; no hay problemas con el DMA
acc_mult1: MOV CL,AL
acc_mult2: AND CL,CL
JZ acc_mult3 ; primer sector problemático
MOV AH,sector
MOV sector_ini,AH
ADD AH,CL
DEC AH
MOV sector_fin,AH
INC AH
SUB AL,CL
CALL acceso_sector ; sectores no problemáticos
MOV sector,AH
JC acc_mult_fin
acc_mult3: AND AL,AL ; ahora el sector problemático
JZ acc_mult_fin
ADD secciones,BL ; compensar futuro decremento
CALL acceso_secc ; a través del buffer auxiliar
JC acc_mult_fin
DEC AL
JMP acc_mult1 ; sectores que restan
acc_mult_fin: RET
ejecuta_io ENDP
; ------------ Mover secciones desde el buffer hacia la memoria (con
; orden F_READ) después de la lectura o de la memoria al
; buffer (orden F_WRITE) antes de la escritura. En la
; verificación (orden F_VERIFY) no se mueve nada porque
; esta subrutina no es invocada.
trans_secc PROC
XPUSH <AX, BX, CX, SI> ; *
MOV BL,seccion ; desde esta sección
CALL num_secciones ; nº secciones del sector
otra_secci: PUSH BX
MOV BH,BL
SHL BH,1
MOV BL,0 ; sección * 512
ADD BX,buffer ; dirección
MOV SI,BX
MOV CX,256 ; tamaño sección (palabras)
CALL swap_reg ; ¿intercambiar origen-destino?
REP MOVSW ; copiar 512 bytes
CALL swap_reg ; ¿intercambiar origen-destino?
POP BX
DEC secciones ; una menos
JZ fin_secc
INC BX ; otra sección del sector
CMP BL,AL ; ¿sector agotado?
JB otra_secci ; aún no
fin_secc: XPOP <SI, CX, BX, AX> ; *
RET
swap_reg: CMP CS:orden,F_WRITE
JE interc
CLC
RET
interc: XCHG SI,DI ; en escritura, invertir el
XPUSH <ES, DS> ; sentido de la operación
XPOP <ES, DS>
RET
trans_secc ENDP
; ------------ Comprobar si el sector ya está en el buffer.
leido? PROC
PUSH AX
MOV AL,buf_unidad
CMP AL,unidad
JNE no_leido ; es en otra unidad
MOV AL,cilindro
MOV AH,cabezal
CMP AX,buf_cilcab
JNE no_leido ; es en otro cilindro/cabezal
MOV AL,buf_sector
CMP AL,sector
JNE no_leido ; es otro sector
POP AX
RET ; está en el buffer
no_leido: STC
POP AX
RET ; sector no leído
leido? ENDP
; ------------ Leer o escribir sector(es). Se selecciona el tamaño de
; sector correcto antes de llamar a sector_io. En esta
; rutina se actualiza la variable «status» en función de
; los posibles errores de acceso. Si sector_fin es
; distinto de 0 se accede a los sectores indicados, si es
; 0 se accede sólo al sector «sector» a través del buffer
; intermedio y al final se anota el sector cargado ó
; escrito para evitar futuras lecturas innecesarias, a
; modo de mini-caché que dispara la velocidad de acceso a
; sectores lógicos consecutivos.
acceso_sector PROC
XPUSH <AX, BX>
CALL seek_drv ; posicionar el cabezal
JNC en_pista
CMP status,0 ; ¿error ya determinado?
JNE acc_fin_err
OR status,40h ; no: pues 'seek error'
acc_fin_err: STC
JMP acceso_fin
en_pista: CALL pista0?
MOV AL,2
JZ tam_acc_ok ; sectores 512 en cil./cab. 0
LEA BX,[SI].tabla_tsect
ADD BL,sector
ADC BH,0
MOV AL,[BX-1]
tam_acc_ok: MOV tsector,AL
CMP sector_fin,0 ; ¿usar buffer intermedio?
JE acceso_buffer
CALL sector_io
MOV sector_fin,0 ; no acceder a más de 1 sector
PUSHF ; **1
JMP acceso_rep ; en el futuro (por defecto)
acceso_buffer: XPUSH <ES, DI>
PUSH CS
POP ES
MOV DI,buffer ; acceso con buffer auxiliar
MOV AL,sector ; mismo sector inicial/final
MOV sector_ini,AL
MOV sector_fin,AL
CALL sector_io
MOV sector_fin,0
XPOP <DI, ES>
PUSHF ; **2
MOV AL,-1 ; invalidar contenido buffer
JC acceso_anota ; si hay error
CMP orden,F_VERIFY
JE acceso_rep ; nada leído físicamente
MOV AL,unidad
acceso_anota: MOV buf_unidad,AL
MOV AL,cilindro
MOV AH,cabezal
MOV buf_cilcab,AX
MOV AL,sector
MOV buf_sector,AL ; anotado el sector en buffer
acceso_rep: POPF ; ** mucho cuidado con la pila
CALL set_err ; ajustar variable «status»
acceso_fin: XPOP <BX, AX>
RET
acceso_sector ENDP
; ------------ Devolver el número de secciones del sector en curso.
num_secciones PROC
CALL pista0?
MOV AL,1
JZ num_secc_ok ; sectores 512 en cil./cab. 0
XPUSH <BX, CX>
LEA BX,[SI].tabla_tsect
ADD BL,sector
ADC BH,0
MOV CL,[BX-1]
SUB CL,2
MOV AL,1
SHL AL,CL
XPOP <CX, BX>
num_secc_ok: RET ; resultado en AL
num_secciones ENDP
; ------------ Asegurar que el motor está en marcha.
motor_ok PROC
XPUSHA ; *
PUSH DS ; **
MOV BX,40h
PUSH BX
POP DS
MOV CH,255-18 ; CH = 255 - 1 segundo
CLI
MOV CL,CS:unidad
MOV AL,1
SHL AL,CL
TEST [BX-1],AL ; ¿motor en marcha?
JZ arrancarlo ; arrancarlo
CMP [BX],CH ; Si encendido y acelerado...
JBE ok_motor ; ...seguir
arrancarlo: OR [BX-1],AL ; arrancar motor
AND BYTE PTR [BX-1],0CFh ; borrar número unidad
MOV AL,CL
XSHL AL,4 ; unidad << 4
OR [BX-1],AL ; nuevo número de unidad
MOV BYTE PTR [BX],255 ; asegurar que no se pare
STI
MOV DX,3F2h ; registro de salida digital
ADD CL,4
MOV AL,1
SHL AL,CL ; colocar bit del motor
OR AL,CS:unidad ; seleccionar unidad
OR AL,00001100b ; modo DMA, no hacer reset
OUT DX,AL ; poner en marcha el motor
MOV AX,90FDh
CLC
INT 15h ; permitir multitarea
JC ok_motor ; timeout
MOV AX,1000 ; 1 segundo aceleración
CALL retardo ; esperar aceleración disco
ok_motor: MOV [BX],CH ; cuenta máxima detención motor
STI ; sin forzar futura aceleración
POP DS ; **
XPOPA ; *
RET
motor_ok ENDP
; ------------ Establecer modalidad de operación del controlador
; y poner el motor en marcha. Si CF=1 se le da tiempo
; además a la unidad para que acelere.
reset_drv PROC
XPUSHA
CALL motor_off_cnt ; cuenta detención motor
MOV CL,unidad
MOV AL,CL
XSHL AL,2 ; unidad seleccionada
OR AL,1 ; bit de motor
SHL AL,CL ; colocar dicho bit
PUSH DS ; *
MOV DX,40h
MOV DS,DX
CLI
MOV DS:[3Fh],AL
AND BYTE PTR DS:[3Eh],70h ; bit IRQ=0 y recalibrar
POP DS ; *
XSHL AL,4 ; bits motor en nibble alto
OR AL,CL ; seleccionar unidad
OR AL,00001000b ; interrupciones+DMA y reset
MOV DX,3F2h ; registro de salida digital
OUT DX,AL ; señal de reset
OR AL,00000100b
MOV CX,50
respiro: LOOP respiro
OUT DX,AL ; fin de señal de reset
CALL espera_int ; rehabilitará interrupciones
MOV AL,8
CALL fdc_write ; comando 'leer estado int...'
CALL fdc_read
CALL fdc_read
CALL envia_specify ; comando 'specify' adecuado
XPOPA
RET
reset_drv ENDP
; ------------ Enviar comando specify a la controladora. El step-rate
; se selecciona según la densidad, para evitar un sonido
; extraño al posicionar o recalibrar el cabezal.
envia_specify PROC
PUSH AX
PUSH DS
MOV AX,40h
MOV DS,AX
MOV AH,DS:[8Bh]
POP DS
MOV AL,3 ; comando 'specify'
CALL fdc_write
MOV AL,0BFh ; step rate para 500 kbps
AND AH,11000000b
JZ spec1_ok
MOV AL,0AFh ; step rate para 1 Mbps
CMP AH,11000000b
JE spec1_ok
MOV AL,0DFh ; step rate para 250/300 Kbps
spec1_ok: CALL fdc_write
MOV AL,2
CALL fdc_write ; head load y modo DMA
POP AX
RET
envia_specify ENDP
; ------------ Recargar cuenta para la detención del motor. Si CF=1 al
; entrar, se establece la mayor cuenta posible; en caso
; contrario, se pone el valor normal de la tabla base.
motor_off_cnt PROC
XPUSH <AX, BX>
PUSH DS
MOV AL,0FFh ; valor máximo
JC motor_off_ok
XOR BX,BX
MOV DS,BX
LDS BX,DWORD PTR DS:[1Eh*4] ; DS:BX -> INT 1Eh
MOV AL,[BX+2] ; byte 2 tabla base disco
motor_off_ok: MOV BX,40h
MOV DS,BX
MOV BYTE PTR DS:[40h],AL ; cuenta parada motor
POP DS
XPOP <BX, AX>
RET
motor_off_cnt ENDP
; ------------ Llevar el cabezal a la pista indicada, recalibrando si
; hubo un reset (se invocó la función 0 de la INT 13h o
; se ejecutó reset_drv) antes de esta operación. Primero
; se selecciona la velocidad de transferencia y se borra
; el resultado de cualquier operación anterior, para que
; todo quede listo para el próximo acceso a disco.
seek_drv PROC
XPUSHA
CALL set_rate ; velocidad / borrar resultados
CALL envia_specify ; comando 'specify' adecuado
MOV AH,1
MOV CL,unidad
SHL AH,CL ; AH = 1 (A:) ó 2 (B:)
PUSH DS
MOV CX,40h
MOV DS,CX
TEST AH,DS:[3Eh]
POP DS
JNZ do_seek ; la unidad ya fue recalibrada
CALL recalibrar
JC fallo_seek ; fallo al recalibrar
do_seek: MOV BX,94h
ADD BL,unidad
MOV AL,cilindro
PUSH DS ; *
MOV CX,40h
MOV DS,CX
OR DS:[3Eh],AH ; unidad ya recalibrada
MOV AH,DS:[41h] ; código de error previo
CMP AL,[BX]
MOV [BX],AL
POP DS ; *
JNE hacer_seek ; seek necesario
CMP AH,40h ; ¿error de seek previo?
JNE seek_ok ; no, evitar seek innecesario
hacer_seek: MOV AL,0Fh
CALL fdc_write ; comando 'seek'
JC fallo_seek
MOV AL,cabezal
XSHL AL,2
OR AL,unidad
CALL fdc_write ; enviar HD, US1, US0
MOV AL,cilindro
CALL fdc_write ; enviar cilindro
CALL espera_int ; esperar interrupción
JC fallo_seek
MOV AL,8
CALL fdc_write ; comando 'leer estado int...'
JC fallo_seek
CALL fdc_read ; leer registro de estado 0
JC fallo_seek
MOV AH,AL
CALL fdc_read ; leer cilindro actual
TEST AH,11000000b ; comprobar ST0
JNZ fallo_seek
MOV AL,15 ; estabilización para escritura
CMP orden,F_WRITE
JE rseek_ok
MOV AL,1 ; estabilización para lectura
rseek_ok: CBW ; AH = 0
CALL retardo ; esperar asentamiento cabezal
seek_ok: CLC ; retornar con éxito
JMP seek_ret
fallo_seek: STC ; retornar indicando fallo
seek_ret: XPOPA
RET
seek_drv ENDP
; ------------ Establecer velocidad de transferencia correcta si aún
; no ha sido seleccionada y borrar el resultado de otra
; operación previa.
set_rate PROC
XPUSHA
CALL pista0?
MOV AX,[SI].vunidad ; velocidad pista 0 / demás
JZ vel_ok
MOV AL,AH
vel_ok: PUSH DS ; *
MOV CX,40h
MOV DS,CX
MOV AH,DS:[8Bh]
MOV CL,6
SHR AH,CL ; aislar bits de velocidad
CMP AL,AH
JE vel_set ; velocidad ya seleccionada
MOV DX,3F7h
OUT DX,AL ; seleccionarla
SHL AL,CL
AND BYTE PTR DS:[8Bh],00111111b
OR DS:[8Bh],AL
vel_set: POP DS ; *
LEA DI,status
MOV CX,8
borra_status: MOV [DI],CH ; borrar información de estado
INC DI
LOOP borra_status
XPOPA
RET
set_rate ENDP
; ------------ Recalibrar la unidad (si hay error se intenta otra vez
; para el caso de que deba moverse más de 77 pistas).
recalibrar PROC
XPUSHA
MOV BX,94h
ADD BL,unidad
PUSH DS ; *
MOV CX,40h
MOV DS,CX
MOV [BX],BH ; pista actual = 0
POP DS ; *
MOV CX,2 ; dos veces como mucho
recalibra: MOV AL,7
CALL fdc_write ; comando de 'recalibrado'
JC fallo_recal
MOV AL,cabezal
XSHL AL,2
OR AL,unidad
CALL fdc_write ; enviar HD, US1, US0
JC fallo_recal
CALL espera_int ; esperar interrupción
JC fallo_recal
MOV AL,8
CALL fdc_write ; comando 'leer estado int...'
JC fallo_recal
CALL fdc_read ; leer registro de estado 0
JC fallo_recal
MOV AH,AL
CALL fdc_read ; leer cilindro actual
XOR AH,00100000b ; bajar bit de 'seek end'
TEST AH,11110000b ; comprobar resultado y ST0
JNZ fallo_recal ; sin 'seek end' o TRK0
MOV AX,1 ; pausa de 1 ms
CALL retardo
JMP recal_ret
fallo_recal: LOOP recalibra ; reintentar comando
STC ; condición de fallo
recal_ret: XPOPA
RET
recalibrar ENDP
; ------------ Cargar o escribir sector(es) del disco en ES:DI,
; actualizando la dirección en ES:DI pero sin alterar
; ningún otro registro. Si hay error se devuelve CF=1 y
; no se modifica ES:DI. A partir de fdc_result se dejan
; los 7 bytes que devuelve el FDC al final del acceso.
; En caso de verificación (F_VERIFY) se programa el DMA
; para que no realice transferencia física (convenio de
; las BIOS con fecha 15/11/85 y posterior).
sector_io PROC
XPUSH <AX, BX, CX, DX>
MOV CL,tsector
MOV CH,0
STC
RCL CH,CL
MOV CL,0 ; nº de bytes por sector
MOV AL,sector_fin
SUB AL,sector_ini
INC AX
CBW ; AX sectores (AH = 0)
MUL CX
MOV DX,AX ; bytes totales
MOV CX,AX
DEC CX ; bytes totales - 1
MOV AX,ES
CALL calc_dir_DMA ; AX:DI -> base BX y página AH
MOV AL,orden ; modo DMA necesario
CALL prepara_DMA
CMP AL,F_WRITE
MOV AL,11000101b ; comando de escritura del FDC
JE orden_io_ok
MOV AL,11100110b ; comando leer (verif.) del FDC
orden_io_ok: CALL fdc_write ; comando leer/escribir del FDC
JC sector_io_ko
MOV AL,cabezal
XSHL AL,2
OR AL,unidad
CALL fdc_write ; byte 1 de la orden
MOV AL,cilindro
CALL fdc_write ; enviar cilindro
MOV AL,cabezal
CALL fdc_write ; enviar cabezal
MOV AL,sector_ini
CALL fdc_write ; enviar nº sector
MOV AL,tsector
CALL fdc_write ; longitud sector
MOV AL,sector_fin
CALL fdc_write ; último sector
MOV AL,[SI].gap
CALL fdc_write ; GAP de lectura/escritura
MOV AL,128
CALL fdc_write ; tamaño sector si longitud=0
CALL espera_int
PUSHF ; *
LEA BX,fdc_result
MOV CX,7
sect_io_res: CALL fdc_read ; leyendo resultados
MOV [BX],AL
INC BX
LOOP sect_io_res
POPF ; *
JC sector_io_ko
TEST fdc_result,11000000b
JNZ sector_io_ko
ADD DI,DX ; actualizar dirección
CLC ; Ok
JMP sector_io_fin
sector_io_ko: STC ; indicar fallo
sector_io_fin: XPOP <DX, CX, BX, AX>
RET
sector_io ENDP
; ------------ Devolver en AH la página de DMA y en BX la base. A la
; entrada, AX:DI -> dirección de memoria y CX = bytes-1.
; Se supone que el buffer no cruza una frontera de DMA.
calc_dir_DMA PROC
PUSH DX
MOV BX,16
MUL BX
ADD AX,DI
ADC DX,0 ; DX:AX = dirección 20 bits
MOV BX,AX ; base en BX
MOV AH,DL ; página
POP DX
RET
calc_dir_DMA ENDP
; ------------ Crear tabla con información para formatear. En ES:BX
; está el futuro sector de arranque del disquete.
genera_info PROC
XPUSHA
MOV buf_unidad,-1 ; invalidar contenido buffer
MOV SI,buffer
MOV DI,BX
CALL pista0?
JNZ no_cilcab0 ; no es cilindro/cabezal 0
ADD DI,ES:[BX+70] ; DI -> datos pista 0
MOV CL,ES:[DI]
MOV CH,0 ; CX sectores en pista 0
INC DI
MOV AL,ES:[DI] ; GAP para pista 0
MOV AH,0 ; byte de relleno
INC DI
MOV BYTE PTR [SI],2 ; tamaño de sector
MOV BYTE PTR [SI+1],CL ; número de sectores
MOV [SI+2],AX ; GAP / byte de relleno
genera_0: ADD SI,4
MOV AL,cilindro
MOV AH,cabezal
MOV [SI],AX ; datos para cada sector
MOV AL,ES:[DI]
MOV AH,2 ; LOG2 (tamaño)-7
INC DI
MOV [SI+2],AX ; nº de sector / tamaño
LOOP genera_0
JMP geninf_exit
no_cilcab0: ADD DI,ES:[BX+72]
CMP BYTE PTR ES:[BX+65],1
JE info_stv
MOV DL,ES:[DI+2] ; tamaño /F
MOV DH,ES:[DI] ; nº sectores
MOV [SI],DX
XCHG DH,DL ; tamaño en DH
MOV CL,DL
MOV CH,0 ; CX sectores
MOV AL,ES:[DI+1] ; GAP para formatear
MOV AH,0 ; byte relleno /F
MOV [SI+2],AX ; GAP / byte de relleno
PUSH DX
MOV AX,ES:[DI+3]
PUSH AX
ADD AL,AH
MUL cilindro
MOV DX,AX
POP AX
MUL cabezal
ADD AX,DX
XOR DX,DX
MOV BL,ES:[DI]
MOV BH,0
DIV BX ; DL = módulo
SUB DL,ES:[DI]
NEG DL
MOV AL,DL
POP DX ; restaurar tamaño en DH
MOV DL,AL ; primer sector de la pista - 1
MOV BL,ES:[DI] ; nº sectores en la pista
genera_pn: ADD SI,4
INC DX
CMP DL,BL
JBE ns_ok
MOV DL,1 ; empezar desde el 1
ns_ok: MOV AL,cilindro
MOV AH,cabezal
MOV [SI],AX ; datos para cada sector
MOV [SI+2],DX ; nº sector / LOG2 (tamaño)-7
LOOP genera_pn
JMP geninf_exit
info_stv: MOV CH,ES:[DI] ; nº sectores
MOV CL,0 ; CL:CH sectores
MOV [SI],CX ; tamaño (CL=0) y número
XCHG CH,CL ; CX sectores
MOV AL,ES:[DI+1] ; GAP para formatear
MOV AH,4Eh ; byte de relleno /M
MOV [SI+2],AX ; GAP / byte de relleno
MOV DL,128
genera_otro: ADD SI,4
INC DX
MOV AL,cilindro
MOV AH,cabezal
MOV [SI],AX ; datos para cada sector
XPUSH <CX, DI> ; *
MOV CL,ES:[DI+2] ; CH está a 0
busca_num: ADD DI,3
CMP DL,ES:[DI]
MOV AX,ES:[DI+1] ; número de sector / tamaño
JE hallado ; es sector a cambiar número
LOOP busca_num
MOV AL,DL ; no cambiar número
MOV AH,0 ; e indicar tamaño 128
hallado: XPOP <DI, CX> ; *
MOV [SI+2],AX ; nº sector / LOG2 (tamaño)-7
LOOP genera_otro
geninf_exit: XPOPA
RET
genera_info ENDP
; ------------ Formatear una pista.
formatea_pista PROC
XPUSHA
MOV BX,buffer
MOV DI,BX
MOV CL,[BX+1]
MOV CH,0 ; CX sectores
XSHL CX,2
DEC CX ; nº de bytes - 1
MOV AX,DS
CALL calc_dir_DMA ; AX:DI -> base BX y página AH
MOV AL,4Ah ; modo DMA para escribir
ADD BX,4 ; saltar primeros 4 bytes
CALL prepara_DMA
MOV BX,buffer
MOV AL,F_FORMAT
CALL fdc_write
JC fallo_fmt
MOV AL,cabezal
XSHL AL,2
OR AL,unidad
CALL fdc_write ; byte 1 de la orden
JC fallo_fmt
MOV CX,4
format_cmd: MOV AL,[BX]
CALL fdc_write
INC BX
LOOP format_cmd
CALL espera_int
fallo_fmt: PUSHF
LEA BX,fdc_result
MOV CX,7
format_res: CALL fdc_read ; leyendo resultados
MOV [BX],AL
INC BX
LOOP format_res
POPF
JC fallo_format
TEST fdc_result,11000000b
JZ format_ret
fallo_format: STC ; fallo
format_ret: XPOPA
RET
formatea_pista ENDP
; ------------ Esperar interrupción de disquete durante casi 2
; segundos antes de considerar que ha sido un fracaso.
espera_int PROC
STI
XPUSHA
XPUSH <DS, 40h>
POP DS
MOV AH,0FFh
esperar_int: CMP AL,DS:[6Ch]
JE mira_int
MOV AL,DS:[6Ch]
INC AH
CMP AH,37 ; ¿más de 2 segundos?
JB mira_int
timeout_int: OR CS:status,80h ; timeout
STC
JMP fin_espera
mira_int: TEST BYTE PTR DS:[3Eh],80h
JZ esperar_int
AND BYTE PTR DS:[3Eh],7Fh ; CF=0
fin_espera: POP DS
XPOPA
RET
espera_int ENDP
; ------------ Preparar DMA para E/S. A la entrada, BX = dirección de
; base, AH = registro de página y CX = nº bytes - 1.
prepara_DMA PROC
PUSH AX
CLI
OUT 0Bh,AL ; registro de modo del DMA
MOV AL,0
DELAY
OUT 0Ch,AL ; clear first/last flip-flop
MOV AL,BL
DELAY
OUT 4,AL
MOV AL,BH
DELAY
OUT 4,AL ; enviada dirección base
DELAY
MOV AL,AH
OUT 81h,AL ; registro de página del DMA
MOV AL,CL
DELAY
OUT 5,AL
MOV AL,CH
DELAY
OUT 5,AL ; enviada cuenta de bytes
STI
MOV AL,2
DELAY
OUT 0Ah,AL ; habilitar canal 2 de DMA
POP AX
RET
prepara_DMA ENDP
; ------------ Recibir byte del FDC en AL. A la vuelta, CF=1 si
; la operación fracasó (el FDC no estaba listo) y
; se indica la condición de timeout en «status».
fdc_read PROC
XPUSH <CX, DX>
MOV DX,3F4h ; registro de estado del FDC
XOR CX,CX ; evitar cuelgue total si falla
espera_rd: IN AL,DX ; leer registro de estado
TEST AL,80h ; ¿bit 7 inactivo?
LOOPZ espera_rd ; así es: el FDC está ocupado
JCXZ fdc_rd_nok
INC DX ; apuntar al registro de datos
IN AL,DX ; leer byte del FDC
CLC
XPOP <DX, CX>
RET
fdc_rd_nok: OR status,80h ; timeout
STC
XPOP <DX, CX>
RET
fdc_read ENDP
; ------------ Enviar byte AL al FDC. A la vuelta, CF=1 si
; la operación fracasó (el FDC no estaba listo) y
; se indica la condición de timeout en «status».
fdc_write PROC
XPUSH <AX, CX, DX>
MOV DX,3F4h ; registro de estado del FDC
XCHG AH,AL ; preservar AL en AH
XOR CX,CX ; evitar cuelgue total si falla
espera_wr: IN AL,DX ; leer registro de estado
TEST AL,80h ; ¿bit 7 inactivo?
LOOPZ espera_wr ; así es: el FDC está ocupado
JCXZ fdc_wr_nok
XCHG AH,AL ; recuperar el dato de AL
INC DX ; apuntar al registro de datos
OUT DX,AL ; enviar byte al FDC
XPOP <DX, CX, AX>
CLC
RET
fdc_wr_nok: OR status,80h ; timeout
XPOP <DX, CX, AX>
STC
RET
fdc_write ENDP
; ------------ Esperar exactamente AX milisegundos.
retardo PROC
PUSHF
XPUSH <AX, BX, CX, DX>
retarda_mas: CMP AX,54 ; como máximo 54 ms cada vez
JBE retarda_fin
PUSH AX
MOV AX,54
CALL rt_ax
POP AX
SUB AX,54
JMP retarda_mas
retarda_fin: CALL rt_ax
XPOP <DX, CX, BX, AX>
POPF
RET
rt_ax: MOV DX,1000 ; retardo de hasta 54 ms
MUL DX
MUL CS:tbase
MOV CX,54925
DIV CX ; AX = contador iteraciones
MOV CX,AX
EVEN ; forzar alineamiento
retarda: DEC CX
JMP SHORT $+2
JNZ retarda
RET
retardo ENDP
; --- Ubicación del sector de hasta 2048 bytes.
EVEN
buffer_io EQU $
tbuffer EQU 2048
fin_residente EQU $ ; fin del área residente sin contar el buffer
bytes_resid EQU fin_residente-ini_residente
; *****************************
; * *
; * I N S T A L A C I O N *
; * *
; *****************************
main PROC
CALL obtener_param ; analizar posibles parámetros
JNC params_ok ; son correctos
OR error,MALSINTAX
JMP informar
params_ok: CALL inic_general ; inicializar ciertas variables
CALL pc_ok?
TEST error,MALPC or MALDOS or MALBIOS or MALDRV
JZ proc_tsr
JMP informar ; no hay configuración correcta
proc_tsr: CMP param_u,ON ; ¿se solicita desinstalarlo?
JNE cont_ins ; no
CALL residente? ; sí: ¿está residente?
JNC desinstalable
OR error,NOINSTALADO ; programa aún no instalado
JMP informar
desinstalable: CALL testWin
JNC win_out_ok
OR error,WIN_UNLOAD
JMP informar ; no desinstalar desde Windows
win_out_ok: OR accion,DESINSTALADO
MOV ES,tsr_seg
MOV DI,tsr_off
TEST BYTE PTR ES:[DI-10],2
JZ no_sys
OR error,INSTALADOSYS ; no desinstalar versión SYS
JMP informar
no_sys: MOV AH,ES:[DI-9]
CALL mx_unload ; desinstalarlo:
JNC informar ; ha sido posible
OR error,IMPDESINS ; no es posible
JMP informar
cont_ins: CALL residente?
JNC ya_reside
CMP AX,0 ; ¿reside una versión distinta?
JE instalable
OR error,NOINSTALABLE ; versión incompatible
JMP informar
ya_reside: OR accion,YAINSTALADO
CALL adaptar_param ; parámetros en copia residente
JMP informar
instalable: CALL testWin
JNC win_in_ok
OR error,WIN_LOAD
JMP informar ; no instalar desde Windows
win_in_ok: OR accion,INSTALADO
CALL instalar_tsr ; instalar el TSR
MOV ES,tsr_seg
informar: CALL info
MOV DX,mem640 ; tamaño zona residente
AND DX,DX
JZ fin_noresid
MOV AX,3100h
INT 21h ; terminar residente
fin_noresid: MOV AX,4C00h
INT 21h ; terminar no residente
main ENDP
instalar_tsr PROC
CALL mx_get_handle ; obtener entrada Multiplex
JNC handle_ok
OR error,MX64FULL ; no quedan entradas
RET
handle_ok: MOV multiplex_id,AH ; entrada multiplex para 2M
CALL preservar_ints ; tomar nota de vectores
CMP param_ml,ON ; ¿se indicó parámetro /ML?
JE instalar_ml ; en efecto
MOV AX,parrafos_resid ; área residente sin PSP
CALL UMB_alloc ; pedir memoria superior XMS
JC busca_upper ; no hay la suficiente
LEA BX,buffer_io-256 ; AX:BX dirección del buffer
CALL testDMA
PUSH AX
MOV DX,AX
MOV AH,12h
CALL gestor_XMS ; redimensionar bloque memoria
CMP AX,1
JE xms_redim_ok
MOV AX,mem640
MOV parrafos_resid,AX ; función no soportada
xms_redim_ok: POP AX
CLC ; indicar que usa memoria XMS
JMP instalar_umb
busca_upper: MOV AX,parrafos_resid
CALL UPPER_alloc ; pedir memoria superior DOS 5
JC instalar_ml ; no hay la suficiente
LEA BX,buffer_io-256 ; AX:BX dirección del buffer
CALL testDMA
XPUSH <AX, ES>
MOV ES,AX
MOV AH,4Ah
INT 21h ; redimensionar bloque memoria
CALL upper_fork ; dejar residente el bloque
XPOP <ES, AX>
STC ; indicar que usa memoria DOS
instalar_umb: MOV ES,AX ; segmento del bloque UMB
MOV DI,0 ; ES:0 zona a donde reubicar
MOV mem640,DI ; no terminar residente
JMP instalar_asi
instalar_ml: ADD parrafos_resid,6 ; respetar 96 bytes de PSP
MOV AX,CS
LEA BX,buffer_io-(256-96)
CALL testDMA
MOV mem640,BX ; ocupar memoria convencional
STC
MOV DI,96 ; instalación mem. convencional
CALL free_environ ; liberar espacio de entorno
instalar_asi: CALL inicializa_id ; inicializar identificación
CALL reubicar_prog ; reubicar programa a ES:DI
CALL activar_ints ; interceptar vectores
RET
instalar_tsr ENDP
;*********************************************************
;* *
;* SUBRUTINAS DE PROPOSITO GENERAL PARA LA INSTALACION *
;* *
;*********************************************************
; ------------ Extraer posibles parámetros de la línea de comandos.
obtener_param PROC
CALL param_mays ; mayusculizar parámetros
MOV BX,81h ; apuntar a zona de parámetros
otro_pmt_mas: CALL saltar_esp ; saltar delimitadores
JNC otro_pmt ; quedan más parámetros
CMP param_ayuda,ON
JE mal_proc_pmt ; 'error' de ayuda
CLC ; parámetros bien procesados
RET
otro_pmt: CMP AL,'/'
JE pmt_barrado ; parámetro precedido por '/'
CMP AL,'?'
JNE mal_proc_pmt
pmt_hlp: MOV param_ayuda,ON
INC BX
JMP otro_pmt_mas
pmt_barrado: INC BX
MOV AL,[BX] ; letra del parámetro
CMP AL,13 ; ¿fin de mandatos?
JE mal_proc_pmt ; falta parámetro
CMP AL,'?'
JE pmt_hlp
CMP AL,'H'
JE pmt_hlp
CMP AL,'U'
JE pmt_U ; parámetro /U
CMP AL,'I'
JE pmt_I ; parámetro /I
CMP AL,'W'
JE pmt_W ; parámetro /W
MOV SI,[BX] ; ¿parámetro de dos caracteres?
CMP SI,"LM"
JE pmt_ML ; parámetro /ML
mal_proc_pmt: STC ; error en parámetro(s)
RET
pmt_U: MOV param_u,ON
INC BX
JMP otro_pmt_mas
pmt_I: MOV param_i,ON
INC BX
JMP otro_pmt_mas
pmt_W: MOV param_w,ON
INC BX
JMP otro_pmt_mas
pmt_ML: MOV param_ml,ON
ADD BX,2
JMP otro_pmt_mas
obtener_param ENDP
; ------------ Poner en mayúsculas los parámetros.
param_mays PROC
MOV BX,81h
otra_mays: MOV AL,[BX]
CMP AL,13
JNE mays_mas
RET
mays_mas: CMP AL,'a'
JB mayusc_ok
CMP AL,'z'
JA mayusc_ok
SUB AL,32
mayusc_ok: MOV [BX],AL
INC BX
JMP otra_mays
param_mays ENDP
; ------------ Saltar espacios, tabuladores,... buscando un parámetro.
saltar_esp: MOV AX,[BX]
INC BX
CMP AL,9 ; carácter tabulador
JE saltar_esp
CMP AL,32 ; espacio en blanco
JE saltar_esp
CMP AL,0Dh ; fin de zona de parámetros
JE fin_param
DEC BX ; puntero al primer carácter
CLC ; hay parámetro
RET
fin_param: STC ; no hay parámetro
RET
; ------------ Ya está instalada otra versión distinta del programa.
error_version PROC
PUSH ES
LEA DX,mal_ver_txt1
CALL print
LES DI,tsr_dir
MOV AL,':'
MOV CL,255
CLD
REPNE SCASB
REPNE SCASB
MOV DL,ES:[DI] ; número de versión
MOV AH,2
INT 21h
MOV DL,'.'
MOV AH,2
INT 21h
MOV DL,ES:[DI+2] ; revisión
MOV AH,2
INT 21h
LEA DX,mal_ver_txt2
CALL print
POP ES
RET
error_version ENDP
; ------------ Inicializar ciertas variables. La memoria consumida
; se calcula para el peor de los casos, cuando el buffer
; residente ocupa incluso el doble debido al DMA.
inic_general PROC
CALL inic_XMS ; detectar controlador XMS
MOV AX,(bytes_resid+tbuffer*2+15)/16
MOV parrafos_resid,AX ; memoria máxima necesaria
CALL testAT
JNC skip_tmtest ; en AT no calcularlo [*]
CALL cte_tiempos
MOV tbase,AX ; cte. retardo para 1/18,2 seg.
skip_tmtest: MOV DL,0
CALL tipo_disco
JC err_drv
AND DL,DL
JZ err_drv ; no hay disqueteras
MOV info_A.tipo_drv,AL ; guardar tipo unidad A:
CMP DL,1
JE fin_inic ; no existe unidad B:
MOV DL,1
CALL tipo_disco
JC fin_inic
MOV info_B.tipo_drv,AL ; guardar tipo unidad B:
RET
err_drv: MOV AX,MALBIOS
OR error,AX
fin_inic: RET
tipo_disco: PUSH ES
MOV AH,8
MOV BL,0
INT 13h
JC tipo_dsk_err
MOV AL,BL
AND AL,AL
JZ tipo_dsk_err
CMP AL,4
JB tipo_dsk_ok
MOV AL,4 ; 1.44/2.88 indistinto
tipo_dsk_ok: CLC
POP ES
RET
tipo_dsk_err: STC
POP ES
RET
inic_general ENDP
; ------------ Considerar presencia de controlador XMS.
inic_XMS PROC
PUSH ES
MOV AX,352Fh
INT 21h ; dirección de INT 2Fh en ES:BX
MOV AX,ES
POP ES
AND AX,AX
JZ xms_ausente ; apunta a 0000:XXXX (DOS 2.x)
MOV AX,4300h
INT 2Fh ; chequear presencia XMS
CMP AL,80h
JNE XMS_ausente ; no instalado
PUSH ES
MOV AX,4310h
INT 2Fh ; sí: obtener su dirección
MOV XMS_off,BX ; y preservarla
MOV XMS_seg,ES
MOV xms_ins,ON
POP ES
RET
XMS_ausente: MOV xms_ins,OFF
RET
inic_XMS ENDP
; ------------ Inicializar área «program_id» del programa residente.
; A la entrada, ES:DI = seg:off a donde será reubicado
; y CF=1 si se utiliza memoria superior XMS.
inicializa_id PROC
PUSHF
MOV segmento_real,ES ; anotar segmento del bloque
MOV offset_real,DI ; ídem con el offset
MOV AX,parrafos_resid
MOV longitud_total,AX
MOV AL,1
POPF ; CF=0: usar memoria UMB XMS
JNC info_ok
DEC AL ; usar memoria convencional
info_ok: OR info_extra,AL
RET
inicializa_id ENDP
; ------------ Comprobar si el programa ya reside en memoria. A la
; salida, CF=0 si programa ya reside, con «tsr_seg» y
; «tsr_off» inicializadas apuntando a la cadena de
; identificación de la copia residente. Si CF=1, el
; programa no reside aún (AX=0) o reside pero en otra
; versión distinta (AX=1).
residente? PROC
XPUSH <CX, SI, DI, ES, AX>
LEA DI,autor_nom_ver ; identificación del programa
MOV SI,DI
MOV AL,0
MOV CL,255
CLD
REPNE SCASB
SUB DI,SI
MOV CX,DI ; tamaño autor+programa+versión
MOV AX,1492h
MOV ES,AX
MOV DI,1992h ; ES:DI protocolo de búsqueda
CALL mx_find_tsr ; buscar si está en memoria
MOV tsr_off,DI ; anotar la dirección programa
MOV tsr_seg,ES ; por si estaba instalado
POP AX
JNC resid_ok ; CF=0 -> programa ya residente
POP ES
PUSH ES
LEA DI,autor_nom_ver
MOV SI,DI
MOV AL,':'
MOV CL,255
REPNE SCASB
REPNE SCASB
SUB DI,SI
MOV CX,DI ; tamaño autor+programa
MOV AX,1492h
MOV ES,AX
MOV DI,1992h ; ES:DI protocolo de búsqueda
CALL mx_find_tsr ; buscar si está en memoria
MOV tsr_off,DI ; anotar dirección del programa
MOV tsr_seg,ES ; por si instalada otra versión
MOV AX,0
JC resid_ok ; CF=1, AX=0 -> no residente
MOV AX,1
STC ; CF=1, AX=1 -> sí: otra vers.
resid_ok: XPOP <ES, DI, SI, CX>
RET
residente? ENDP
; ------------ Adaptar parámetros de un 2MX ya instalado.
; Sólo se actualiza la constante de retardo, por si el
; turbo de la máquina fue conmutado.
adaptar_param PROC
PUSH ES
MOV ES,tsr_seg
MOV AX,tbase
MOV ES:tbase,AX
POP ES
RET
adaptar_param ENDP
; ------------ Preservar vectores de interrupción previos.
preservar_INTs PROC
XPUSH <ES, DI>
LEA DI,tabla_vectores
MOV CL,[DI-1]
MOV CH,0 ; CX vectores interceptados
otro_vector: XPUSH <CX, DI>
MOV AH,35h
MOV AL,[DI]
INT 21h ; obtener vector de INT xx
XPOP <DI, CX>
MOV [DI+1],BX ; anotar donde apunta
MOV [DI+3],ES
ADD DI,5
LOOP otro_vector ; repetir con los restantes
XPOP <DI, ES>
RET
preservar_INTs ENDP
; ------------ Liberar espacio de entorno.
free_environ PROC
PUSH ES
MOV ES,DS:[2Ch] ; dirección del entorno
MOV AH,49h
INT 21h ; liberar espacio de entorno
POP ES
RET
free_environ ENDP
; ------------ Reservar bloque de memoria superior del nº párrafos AX,
; devolviendo en AX el segmento donde está. CF=1 si no
; está instalado el gestor XMS (AX=0) o hay un error (AL
; devuelve el código de error del controlador XMS).
UMB_alloc PROC
XPUSH <BX, CX, DX>
CMP xms_ins,ON
JNE no_umb_disp ; no hay controlador XMS
MOV DX,AX ; número de párrafos
MOV AH,10h ; solicitar memoria superior
CALL gestor_XMS
CMP AX,1 ; ¿ha ido todo bien?
MOV AX,BX ; segmento UMB/código de error
JNE XMS_fallo ; fallo
XPOP <DX, CX, BX> ; ok
CLC
RET
no_umb_disp: MOV AX,0
XMS_fallo: XPOP <DX, CX, BX>
STC
RET
UMB_alloc ENDP
; ------------ Reservar memoria superior, con DOS 5.0, del tamaño
; solicitado (AX párrafos). Si no hay bastante CF=1,
; en caso contrario devuelve el segmento en AX.
UPPER_alloc PROC
PUSH AX
MOV AH,30h
INT 21h
CMP AL,5
POP AX
JAE UPPER_existe
STC
JMP UPPER_fin ; necesario DOS 5.0 mínimo
UPPER_existe: PUSH AX ; preservar párrafos...
MOV AX,5800h
INT 21h
MOV alloc_strat,AX ; preservar estrategia
MOV AX,5802h
INT 21h
MOV umb_state,AL ; preservar estado UMB
MOV AX,5803h
MOV BX,1
INT 21h ; conectar cadena UMB's
MOV AX,5801h
MOV BX,41h
INT 21h ; High Memory best fit
POP BX ; ...párrafos requeridos
MOV AH,48h
INT 21h ; asignar memoria
PUSHF
PUSH AX ; guardado el resultado
MOV AX,5801h
MOV BX,alloc_strat
INT 21h ; restaurar estrategia
MOV AX,5803h
MOV BL,umb_state
XOR BH,BH
INT 21h ; restaurar estado cadena UMB
POP AX
POPF
upper_fin: RET
UPPER_alloc ENDP
; ------------ Manipular PID para independizar el bloque de memoria
; superior del programa y dejarlo residente. ES apunta
; al segmento y DS al PSP del programa principal.
upper_fork PROC
XPUSH <DS, ES>
MOV AX,ES
DEC AX
MOV ES,AX
INC AX
MOV WORD PTR ES:[1],AX ; manipular PID
MOV WORD PTR ES:[16],20CDh ; simular PSP
MOV AX,DS
DEC AX
MOV DS,AX
MOV CX,8
MOV SI,CX
MOV DI,CX
CLD
REP MOVSB ; copiar nombre de programa
XPOP <ES, DS>
RET
upper_fork ENDP
; ------------ Reubicar programa residente a su dirección definitiva.
reubicar_prog PROC
PUSH DI
LEA SI,ini_residente
MOV CX,bytes_resid
CLD
ADD SI,2 ; no copiar primera palabra
ADD DI,2 ; respetar primera palabra
SUB CX,2
REP MOVSB
POP DI
RET
reubicar_prog ENDP
; ------------ desviar vectores de interrupción a las nuevas rutinas.
; Se tendrá en cuenta que está ensambladas para correr en
; un offset inicial (100h) y que el offset real en que
; han sido instaladas está en DI. Por ello, CS ha de
; desplazarse (100h-DI)/16 unidades atrás (DI se supone
; múltiplo de 16). El segmento inicial es ES.
activar_INTs PROC
XPUSH <CX, DS> ; preservar DS para el retorno
MOV AX,100h
SUB AX,DI ; AX = 100h-DI
MOV CL,4
SHR AX,CL ; AX = (100h-DI)/16
MOV CX,ES
SUB CX,AX
MOV tsr_seg,CX
MOV DS,CX
LEA SI,offsets_ints
MOV CX,CS:[SI] ; CX vectores a desviar
ADD SI,2
desvia_otro: MOV AL,CS:[SI] ; número del vector en curso
MOV DX,CS:[SI+1] ; obtener offset
MOV AH,25h
INT 21h ; desviar INT xx a DS:DX
ADD SI,3
LOOP desvia_otro
XPOP <DS, CX>
RET
activar_INTs ENDP
; ------------ Buscar entrada no usada en la interrupción Multiplex.
; A la salida, CF=1 si no hay hueco (ya hay 64 programas
; residentes instalados con esta técnica). Si CF=0, se
; devuelve en AH un valor de entrada libre en la INT 2Fh.
mx_get_handle PROC
MOV AH,0C0h
mx_busca_hndl: PUSH AX
MOV AL,0
INT 2Fh
CMP AL,0FFh
POP AX
JNE mx_si_hueco
INC AH
JNZ mx_busca_hndl
STC
RET
mx_si_hueco: CLC
RET
mx_get_handle ENDP
; ------------ Buscar un TSR por la interrupción Multiplex. A la
; entrada, DS:SI cadena de identificación del programa
; (CX bytes) y ES:DI protocolo de búsqueda (normalmente
; 1492h:1992h). A la salida, si el TSR ya está instalado,
; CF=0 y ES:DI apunta a la cadena de identificación del
; mismo. Si no, CF=1 y ningún registro alterado.
mx_find_tsr PROC
MOV AH,0C0h
mx_rep_find: XPUSH <AX, CX, SI, DS, ES, DI>
MOV AL,0
PUSH CX
INT 2Fh
POP CX
CMP AL,0FFh
JNE mx_skip_hndl ; no hay TSR ahí
CLD
PUSH DI
REP CMPSB ; comparar identificación
POP DI
JE mx_tsr_found ; programa buscado hallado
mx_skip_hndl: XPOP <DI, ES, DS, SI, CX, AX>
INC AH
JNZ mx_rep_find
STC
RET
mx_tsr_found: ADD SP,4 ; «sacar» ES y DI de la pila
XPOP <DS, SI, CX, AX>
CLC
RET
mx_find_tsr ENDP
; ------------ Eliminar TSR del convenio si es posible. A la entrada,
; en AH se indica la entrada Multiplex; a la salida, CF=1
; si fue imposible y CF=0 si se pudo. Se corrompen todos
; los registros salvo los de segmento. En caso de fallo
; al desinstalar, AL devuelve el vector «culpable».
mx_unload PROC
PUSH ES
CALL mx_ul_tsrcv?
JNC mx_ul_able
POP ES
RET
mx_ul_able: XOR AL,AL
XCHG AH,AL
MOV BP,AX ; BP=entrada Multiplex del TSR
MOV CX,2
mx_ul_pasada: PUSH CX ; siguiente pasada
LEA SI,tabla_vectores
MOV CL,ES:[SI-1]
MOV CH,0 ; CX = nº vectores
mx_ul_masvect: POP AX
PUSH AX ; pasada en curso
DEC AL
PUSH CX
mx_ul_2f: MOV AL,ES:[SI] ; vector en curso
JNZ mx_ul_pasok
CMP CX,1 ; ¿último vector?
JNE mx_ul_noult
MOV AL,2Fh
LEA SI,tabla_vectores
mx_ul_busca2f: CMP ES:[SI],AL ; ¿INT 2Fh?
JE mx_ul_pasok
ADD SI,5
JMP mx_ul_busca2f
mx_ul_noult: CMP AL,2Fh ; ¿restaurar INT 2Fh?
JNE mx_ul_pasok
ADD SI,5
JMP mx_ul_2f
mx_ul_pasok: XPUSH <ES, AX>
MOV AH,0
SHL AX,1
SHL AX,1
DEC AX
MOV CS:mx_ul_tsroff,AX
MOV CS:mx_ul_tsrseg,0 ; apuntar a tabla vectores
POP AX
PUSH AX
MOV AH,35h
INT 21h ; vector en ES:BX
POP AX
MOV CL,4
SHR BX,CL
MOV DX,ES
ADD DX,BX ; INT xx en DX (aprox.)
MOV AH,0C0h
mx_ul_masmx: CALL mx_ul_tsrcv?
JNC mx_ul_tsrcv
JMP mx_ul_otro
mx_ul_tsrcv: PUSH ES:[DI-16] ; ...TSR del convenio en ES:DI
PUSH ES:[DI-12]
MOV DI,ES:[DI-8] ; offset a la tabla de vectores
MOV CL,ES:[DI-1]
MOV CH,0 ; número de vectores en CX
mx_ul_buscav: CMP AL,ES:[DI]
JE mx_ul_usavect ; este TSR usa vector analizado
ADD DI,5
LOOP mx_ul_buscav
ADD SP,4 ; no lo usa
JMP mx_ul_otro
mx_ul_usavect: XPOP <CX, BX> ; tamaño y segmento del TSR
CMP DX,BX
JB mx_ul_otro ; la INT xx no le apunta
ADD BX,CX
CMP DX,BX
JA mx_ul_otro ; la INT xx le apunta
PUSH AX
XOR AL,AL
XCHG AH,AL
CMP AX,BP ; ¿es el propio TSR?
POP AX
JNE mx_ul_chain ; no
XPOP <ES, CX, BX> ; sí: ¡posible reponer vector!
XPUSH <BX, CX, ES>
DEC BX
JNZ mx_ul_norest ; no es la segunda pasada
POP ES ; segunda pasada...
XPUSH <ES, DS>
MOV BX,CS:mx_ul_tsroff ; restaurar INT's
MOV DS,CS:mx_ul_tsrseg
CLI
MOV CX,ES:[SI+1]
MOV [BX+1],CX
MOV CX,ES:[SI+3]
MOV [BX+3],CX
STI
POP DS
mx_ul_norest: XPOP <ES, CX>
ADD SI,5 ; siguiente vector
DEC CX
JZ mx_unloadable ; no más, ¡desinstal-ar/ado!
JMP mx_ul_masvect
mx_ul_chain: MOV CS:mx_ul_tsroff,DI ; ES:DI almacena la dirección
MOV CS:mx_ul_tsrseg,ES ; de la variable vector
MOV DX,ES:[DI+1]
MOV CL,4
SHR DX,CL
MOV CX,ES:[DI+3]
ADD DX,CX ; INT xx en DX (aprox.)
MOV AH,0BFh
mx_ul_otro: INC AH ; a por otro TSR
JZ mx_ul_exitnok ; ¡se acabaron!
JMP mx_ul_masmx
mx_ul_exitnok: ADD SP,6 ; equilibrar pila
POP ES
STC
RET ; imposible desinstalar
mx_unloadable: POP CX
DEC CX
JZ mx_ul_exitok ; desinstalado
JMP mx_ul_pasada ; 1ª pasada exitosa: por la 2ª
mx_ul_exitok: TEST ES:info_extra,111b ; ¿tipo de instalación?
MOV ES,ES:segmento_real ; segmento real del bloque
JZ mx_ul_freeml ; cargado en RAM convencional
CMP xms_ins,ON
JNE mx_ul_freeml ; no hay controlador XMS (¿?)
MOV DX,ES
MOV AH,11h
CALL gestor_XMS ; liberar memoria superior
POP ES
CLC
RET
mx_ul_freeml: MOV AH,49h
INT 21h ; liberar bloque de memoria ES:
POP ES
CLC
RET
mx_ul_tsrcv?: XPUSH <AX, ES, DI> ; ¿es TSR del convenio?...
MOV DI,1492h
MOV ES,DI
MOV DI,1992h
INT 2Fh
CMP AX,0FFFFh
JNE mx_ul_ncvexit
CMP WORD PTR ES:[DI-4],"#*"
JNE mx_ul_ncvexit
CMP WORD PTR ES:[DI-2],"*#"
JNE mx_ul_ncvexit
ADD SP,4 ; CF=0
POP AX
RET
mx_ul_ncvexit: XPOP <DI, ES, AX> ; ...no es TSR del convenio
STC ; CF=1
RET
mx_ul_tsroff DW 0
mx_ul_tsrseg DW 0
mx_unload ENDP
; ------------ Inicializar variable idioma_sp según idioma del país.
habla_hispana? PROC
XPUSH <AX, BX, CX, DX, BP>
MOV AH,30h
INT 21h
XCHG AH,AL ; AX = versión del DOS
MOV BP,AX
MOV idioma_sp,OFF ; supuesto de habla no hispana
CMP BP,200h
JB habla_ok
LEA DX,buffer_aux
MOV AX,3800h
INT 21h ; obtener información del pais
CMP BP,20Bh
JE habla_ax ; DOS 2.11: AX cód. telefónico
CMP BP,300h
JB habla_ok ; 2.x excepto 2.11: mala suerte
MOV AX,BX
habla_ax: LEA BX,paises_sp-2
MOV CX,numpaises_sp
habla_sp?: ADD BX,2
CMP AX,[BX]
JE habla_hispana
LOOP habla_sp?
habla_ok: MOV AL,param_i
XOR idioma_sp,AL ; considerar parámetro /I
XPOP <BP, DX, CX, BX, AX>
RET
habla_hispana: MOV idioma_sp,ON ; país de habla hispana
MOV AL,param_i
XOR idioma_sp,AL ; considerar parámetro /I
XPOP <BP, DX, CX, BX, AX>
RET
habla_hispana? ENDP
; ------------ Imprimir cadena en DS:DX delimitada por un 0 ó un 255.
; Si hay que imprimir en inglés se toma la cadena que va
; después si ésta acaba en 255 (si acaba en 0, no hay
; distinción entre mensaje castellano e inglés).
print PROC
XPUSH <AX, BX, CX, DX>
pr_decidir: CMP idioma_sp,OFF
JE usar_uk
CMP idioma_sp,ON
JE usar_sp
PUSH DX
CALL habla_hispana? ; determinar lengua
POP DX
JMP pr_decidir
usar_uk: MOV BX,DX
DEC BX
usar_uk?: INC BX
CMP BYTE PTR [BX],0
JE usar_sp ; acaba en 0: no traducir
CMP BYTE PTR [BX],255
JNE usar_uk?
LEA DX,[BX+1] ; acaba en 255: traducir
usar_sp: MOV BX,DX
DEC BX
print_cad: INC BX
CMP BYTE PTR [BX],0
JE prlong_ok
CMP BYTE PTR [BX],255
JNE print_cad ; calcular longitud
prlong_ok: MOV CX,BX
SUB CX,DX
MOV AH,40h
MOV BX,1
INT 21h
XPOP <DX, CX, BX, AX>
RET
print ENDP
; ------------ Informar al usuario.
info PROC
CMP param_ayuda,ON ; ¿solicitud de ayuda?
JNE info_normal
LEA DX,ayuda_txt
CALL print
JMP fin_info
info_normal: LEA DX,programa_txt
CALL print
TEST error,0FFFFh
JZ info_ins ; no hay error
JMP info_err ; lo hay: informar del mismo
info_ins: LEA DX,instalado_txt
TEST accion,INSTALADO ; ¿informar "instalado"?
JNZ acc_ok
LEA DX,ya_instal_txt
TEST accion,YAINSTALADO ; ¿informar "ya instalado"?
JNZ acc_ok
LEA DX,des_ok_txt ; informar "desinstalado"
CALL print
JMP fin_info
acc_ok: CALL print ; instalado/ya instalado...
LEA DX,dma_cross_txt
TEST accion,BUFFERPLUS ; ¿el DMA cruzaba frontera?
JZ dma_ok
CALL print
dma_ok: JMP fin_info
info_err: LEA DX,mal_pc_txt
TEST error,MALPC ; ¿no es ordenador adecuado?
JZ otroerr1
CALL print
otroerr1: LEA DX,mal_dos_txt
TEST error,MALDOS ; ¿DOS incorrecto?
JZ otroerr21
CALL print
otroerr21: LEA DX,mal_bios_txt
TEST error,MALBIOS ; ¿BIOS obsoleta?
JZ otroerr22
CALL print
otroerr22: LEA DX,mal_drv_txt
TEST error,MALDRV ; ¿Unidades de doble?
JZ otroerr23
CALL print
otroerr23: LEA DX,err_sintax_txt
TEST error,MALSINTAX ; ¿error de sintaxis?
JZ otroerr3
CALL print
otroerr3: LEA DX,imp_desins_txt
TEST error,NOINSTALADO ; no instalado, ¿piden
JZ otroerr4 ; desinstalar?
CALL print
otroerr4: LEA DX,des_no_ok_txt
TEST error,IMPDESINS ; ¿imposible desinstalar?
JZ otroerr5
CALL print
otroerr5: LEA DX,inst_sys_txt
TEST error,INSTALADOSYS ; ¿instalada versión *.SYS?
JZ otroerr5x
CALL print
otroerr5x: TEST error,NOINSTALABLE ; ¿versión incorrecta?
JZ otroerr6
CALL error_version
otroerr6: LEA DX,nocabe_txt
TEST error,MX64FULL ; ¿imposible instalar?
JZ otroerr8
CALL print
otroerr8: LEA DX,win_ld_txt
TEST error,WIN_LOAD ; ¿instalar desde Windows?
JZ otroerr9
CALL print
otroerr9: LEA DX,win_ul_txt
TEST error,WIN_UNLOAD ; ¿desinstalar desde Windows?
JZ fin_info
CALL print
fin_info: RET
info ENDP
; ------------ Comprobar que la configuración es la adecuada. Para
; saber si la INT 13h de este ordenador acaba llamando a
; la INT 40h, se desvía la INT 40h y se provoca un inocuo
; reset de disquetes vía INT 13h para comprobar si pasa
; por la INT 40h.
pc_ok? PROC
CALL test_i40
TEST accion,I40
JZ test_dos ; no soportada la INT 40h
MOV nueva_i13,40h
MOV vieja_i13,40h ; usar INT 40 en vez de INT 13
test_dos: MOV AH,30h
INT 21h
XCHG AH,AL
CMP AX,31Eh ; ¿DOS 3.30 o superior?
MOV AX,MALDOS
JB pc_nok
CALL testAT
MOV AX,MALPC
JNC pc_nok ; [*]
TEST error,MALBIOS
JNZ pc_ok ; con ese error vale
MOV AX,MALDRV
CMP info_A.tipo_drv,2 ; ¿unidad A: de 1.2?
JE pc_ok
CMP info_A.tipo_drv,4 ; ¿unidad A: de 1.44 ó 2.88?
JAE pc_ok
CMP info_B.tipo_drv,2 ; ¿unidad B: de 1.2?
JE pc_ok
CMP info_B.tipo_drv,4 ; ¿unidad B: de 1.44 ó 2.88?
JAE pc_ok
pc_nok: OR error,AX
pc_ok: TEST error,MALPC
JZ pc_ok?_ret
AND error,MALPC ; ese error basta
pc_ok?_ret: RET
pc_ok? ENDP
; --- Comprobar si la INT 40h está en uso
test_i40: XPUSH <DS, ES> ; *
MOV AX,3540h
INT 21h
XPUSH <ES, BX> ; vector de INT 40h original
LEA DX,i40_aux
MOV AX,2540h
INT 21h ; establecer nueva INT 40h
XOR AX,AX
MOV DL,0
INT 13h ; reset de disco
XPOP <DX, DS>
MOV AX,2540h
INT 21h ; restaurar INT 40h original
XPOP <ES, DS> ; *
RET
i40_aux PROC
OR CS:accion,I40 ; sí utilizada INT 40h
IRET ; desde la INT 13h
i40_aux ENDP
; --- Detectar 286 ó superior.
testAT PROC
PUSH AX
PUSHF
POP AX
OR AH,70h ; intentar activar bit 12, 13 ó 14
PUSH AX ; del registro de estado
POPF
PUSHF
POP AX
AND AH,0F0h
CMP AH,0F0h
JE testedAT
STC
testedAT: CMC ; CF = 0 en AT y 1 en PC/XT
POP AX
RET
testAT ENDP
; ------------ Comprobar que el buffer para el DMA en la copia
; residente no cruza una frontera. En ese caso se emplea
; otro buffer ubicado tras el habitual, lo que aumenta el
; consumo de memoria de este área. A la entrada AX apunta
; al segmento que contendrá el buffer y BX el offset. Si
; no se produce el cruce, se disminuye «parrafos_resid»
; para economizar memoria; incluso aunque se produzca,
; esta variable se reduce en lo posible. A la vuelta,
; «parrafos_resid» y BX indican la memoria definitiva
; ocupada por el programa.
testDMA PROC
XPUSH <AX, CX, DX>
MOV CX,16
MUL CX
ADD AX,BX
ADC DX,0 ; DX:AX = dirección 20 bits
MOV CX,DX
PUSH AX
ADD AX,tbuffer-1 ; buffer para el mayor sector
ADC DX,0
POP AX
MOV BX,parrafos_resid
SUB BX,tbuffer/16 ; mejor supuesto posible
CMP DX,CX
JE dmatested
NEG AX
ADD AX,15
MOV CL,4
SHR AX,CL
ADD BX,AX ; consumo adicional
SHL AX,CL
ADD buffer,AX ; nueva posición del buffer
OR accion,BUFFERPLUS ; aviso al usuario
dmatested: MOV parrafos_resid,BX
XPOP <DX, CX, AX>
RET
testDMA ENDP
; ------------ Desde Windows, no se permite instalar o desinstalar 2MX
testWin PROC
PUSH AX
CMP param_w,ON ; ¿se indicó parámetro /W?
JE fin_testWin
MOV AX,1600h
INT 2Fh
AND AL,AL ; ¿Windows en modo extendido?
JZ noWinEnh
CMP AL,80h ; ¿Windows en modo extendido?
JE noWinEnh
siWin: STC ; estamos dentro de Windows
JMP fin_testWin
noWinEnh: MOV AX,4680h
INT 2Fh
AND AX,AX
JZ siWin ; Windows en modo real/estándar
fin_testWin: POP AX
RET ; CF=1 si dentro de Windows
testWin ENDP
; ------------ Calcular la constante de retardo básica para perder
; exactamente 54,925 ms. Con una regla de 3 se podrá
; después aplicar para hacer retardos de milisegundos.
cte_tiempos PROC
XPUSH <DS, ES, BX, CX, DX>
MOV AX,3508h
INT 21h
XPUSH <ES, BX> ; preservar vector de INT 8
PUSH DS
MOV AX,40h
MOV DS,AX
MOV AL,DS:[6Ch]
espera_i8: CMP AL,DS:[6Ch]
JE espera_i8 ; esperar INT 8 ... para que no
POP DS
LEA DX,i8_crono ; venga otra en un buen rato...
MOV AX,2508h
INT 21h ; nueva rutina de INT 8
IN AL,21h
PUSH AX ; preservar estado de IRQ's
MOV AL,11111110b
OUT 21h,AL ; permitir sólo IRQ0
MOV AH,0 ; fase
MOV CX,0 ; contador
MOV BX,CX ; seguiría a 0 si fallara
EVEN ; forzar alineamiento
cuenta_iter: DEC CX ; <─┐ bucle básico de retardo
JMP SHORT $+2 ; │
JNZ cuenta_iter ; <─┘ lo interrumpirá INT 8
POP AX ; anterior estado de IRQ's
OUT 21h,AL
XPOP <DX, DS>
PUSH BX ; valor real contado
MOV AX,2508h ; restaurar vector de INT 8
INT 21h
POP AX ; (65536-AX) vueltas en 54,9 ms
NEG AX ; constante de retardo básica
XPOP <DX, CX, BX, ES, DS>
RET
i8_crono: INC AH ; nueva INT 8 que interrumpe
CMP AH,1 ; el bucle de retardo
JE fase1
CMP AH,2
JE fase2
i8_exit: MOV AL,20h
OUT 20h,AL
IRET
fase1: MOV CX,0 ; sincronizar con el reloj
JMP i8_exit
fase2: MOV BX,CX ; anotar constante de retardo
MOV CX,1 ; forzar fin del bucle
JMP i8_exit
cte_tiempos ENDP
; ***********************************************
; * *
; * D A T O S N O R E S I D E N T E S *
; * *
; ***********************************************
ON EQU 1 ; constantes booleanas
OFF EQU 0
; ------------ Gestión de memoria y control de instalación.
xms_ins DB OFF ; a ON si presente controlador XMS
gestor_XMS LABEL DWORD ; dirección del controlador XMS
XMS_off DW 0
XMS_seg DW 0
parrafos_resid DW ? ; párrafos de memoria consumidos
alloc_strat DW 0 ; estrategia asignación (DOS 5)
umb_state DB 0 ; estado de bloques UMB (DOS 5)
tsr_dir LABEL DWORD ; dirección de la copia residente
tsr_off DW 0
tsr_seg DW 0
mem640 DW 0 ; párrafos de memoria convencional
offsets_ints DW 2 ; número de vectores interceptados
nueva_i13 DB 13h ; tabla de offsets de los vectores
DW ges_int13 ; de interrupción interceptados
DB 2Fh
DW ges_int2F
param_ml DB OFF ; a ON si se indicó parámetro /ML
param_u DB OFF ; a ON si se indicó parámetro /U
param_i DB OFF ; a ON si se indicó parámetro /I
param_w DB OFF ; a ON si se indicó parámetro /W
param_ayuda DB OFF ; a ON si se indicó /? /H ó ?
MALPC EQU 1 ; Códigos de error
MALDOS EQU 2
MALBIOS EQU 4
MALDRV EQU 8
MALSINTAX EQU 16
NOINSTALADO EQU 32
IMPDESINS EQU 64
INSTALADOSYS EQU 128
NOINSTALABLE EQU 256
MX64FULL EQU 512
WIN_LOAD EQU 1024
WIN_UNLOAD EQU 2048
INSTALADO EQU 1 ; códigos de acción e información
YAINSTALADO EQU 2
DESINSTALADO EQU 4
BUFFERPLUS EQU 8
I40 EQU 16
error DW 0 ; variable para acumular errores
accion DB 0 ; variable que indica lo sucedido
; ------------ Códigos de modos y órdenes del DMA y del FDC.
F_READ EQU 46h ; modo DMA para lectura
F_WRITE EQU 4Ah ; modo DMA para escritura
F_VERIFY EQU 42h ; modo DMA para verificación
F_FORMAT EQU 01001101b ; orden de formateo del FDC
; ------------ Otras variables.
idioma_sp DB 5Ah ; ni en ON ni en OFF al principio
; --- Código telefónico de países de
; habla hispana (mucha o poca).
paises_sp DW 54 ; Argentina
DW 591 ; Bolivia
DW 57 ; Colombia
DW 506 ; Costa Rica
DW 56 ; Chile
DW 593 ; Ecuador
DW 503 ; El Salvador
DW 34 ; España
DW 63 ; Filipinas
DW 502 ; Guatemala
DW 504 ; Honduras
DW 212 ; Marruecos
DW 52 ; México
DW 505 ; Nicaragua
DW 507 ; Panamá
DW 595 ; Paraguay
DW 51 ; Perú
DW 80 ; Puerto Rico
DW 508 ; República Dominicana
DW 598 ; Uruguay
DW 58 ; Venezuela
DW 3 ; genérico latinoamérica
numpaises_sp EQU ($-OFFSET paises_sp)/2
; ------------ Texto.
programa_txt DB 13,10," 2MX 2.1",0
instalado_txt DB " instalado.",13,10,255," installed.",13,10,0
ya_instal_txt DB " ya instalado, base de tiempos ajustada.",13,10,255
DB " already installed, delay constant adjusted.",13,10,0
nocabe_txt DB ": Instalación imposible.",13,10
DB " Ya hay 64 programas residentes con la "
DB "misma técnica.",13,10,255
DB ": Unable to install.",13,10
DB " There are already 64 TSR's with the same technique."
DB 13,10,0
err_sintax_txt DB 13,10," - Parámetro(s) incorrecto(s)."
DB 13,10," Ejecute 2MX /? para obtener "
DB "ayuda.",13,10,7,255
DB 13,10," - Incorrect option(s)."
DB 13,10," Execute 2MX /? to obtain help."
DB 13,10,7,0
mal_pc_txt DB 13,10," - Error: Necesario ordenador PC/XT. Utilice 2M en esta máquina.",13,10,255
DB 13,10," - Error: Needs PC/XT system. Use 2M on this computer.",13,10,0
mal_dos_txt DB 13,10," - Error: Necesaria versión DOS 3.30 ó posterior.",13,10,255
DB 13,10," - Error: Needs at least DOS 3.30 or above.",13,10,0
mal_bios_txt DB 13,10," - Error: No puedo detectar el tipo de las unidades. Instale 2M-XBIOS antes.",13,10,255
DB 13,10," - Error: Impossible to detect drive types. Please install 2M-XBIOS before.",13,10,0
mal_drv_txt DB 13,10," - Error: Necesaria(s) unidad(es) de alta densidad.",13,10,255
DB 13,10," - Error: Needs high-density floppy drive(s).",13,10,0
dma_cross_txt DB " - Nota: El buffer de E/S cruzaba una frontera de DMA y fue ampliado.",13,10
DB " Cambie la ubicación en memoria de 2MX para ahorrar unos bytes.",13,10,255
DB " - Note: I/O buffer has been extended because 2MX crosses a DMA boundary.",13,10
DB " Modify the memory location of 2MX to save a little memory.",13,10,0
win_ld_txt DB " *NO* instalado:",13,10," Este programa debe cargarse ANTES de entrar en Windows.",13,10,7,255
DB " *NOT* installed:",13,10," This program must be loaded before WINDOWS.",13,10,7,0
win_ul_txt DB " *NO* desinstalado:",13,10," Este programa debe descargarse fuera de Windows.",13,10,7,255
DB " *NOT* uninstalled:",13,10," This program must be uninstalled outside of WINDOWS.",13,10,7,0
mal_ver_txt1 DB 13,10," - Error: ya está instalada la versión ",255
DB 13,10," - Error: Version ",0
mal_ver_txt2 DB " de este programa.",13,10,7,255
DB " of this program is already installed.",13,10,7,0
des_ok_txt DB " desinstalado.",13,10,255," uninstalled.",13,10,0
des_no_ok_txt DB 13,10," - Desinstalación imposible (se ha "
DB "instalado después un programa"
DB 13,10," que no respeta el convenio y tiene "
DB "alguna interrupción común).",13,10,7,255
DB 13,10," - Uninstall unavailable (it has been"
DB " installed before a program that"
DB 13,10," not support CiriSOFT convention"
DB " and uses a common interrupt).",13,10,7,0
imp_desins_txt DB 13,10," - Programa aún no instalado: "
DB "imposible desinstalarlo.",13,10,255
DB 13,10," - Program not installed: "
DB "impossible to uninstall.",13,10,0
inst_sys_txt DB 13,10," - Instalado como controlador de dispositivo; no desinstalable.",13,10,255
DB 13,10," - Installed as device driver: unable to uninstall.",13,10,0
ayuda_txt LABEL BYTE
DB 13,10,10
DB " 2MX 2.1 - CONTROLADOR PARA ACCESO A DISCOS FORMATEADOS CON TECNOLOGIA STV.",13,10
DB " (c) 1994 Ciriaco García de Celis - Grupo Universitario de Informática.",13,10
DB " C/Renedo, 2, 4-C; 47005 Valladolid (España) - ciri@gui.uva.es - 2:341/21.8",13,10,10
DB " Sintaxis: 2MX [/ML] [/U]",13,10,10
DB " ■ 2MX es un programa de tipo «CARDWARE» estando autorizada su distribución sin",13,10
DB " modificaciones y con carácter gratuito. Tras ejecutarlo 2MX se queda residente",13,10
DB " en memoria para dar soporte a disquetes de formato 2M creados por 2MF.",13,10,10
DB " ■ 2MX precisa para operar:",13,10
DB " - Un equipo PC/XT con una BIOS moderna (o 2M-XBIOS instalado en su defecto).",13,10
DB " - Al menos una disquetera de alta densidad (las de doble no son controladas).",13,10
DB " - 5296 bytes de memoria superior o en su defecto 5392 de memoria convencional.",13,10
DB " - Sistema operativo DOS y/o WINDOWS 3.X, otros entornos no son contemplados.",13,10
DB " - Utilidad 2MF para formatear los disquetes 2M.",13,10
DB " ■ 2MX ofrece:",13,10
DB " - Soporte para disquetes de 902K (5¼-DD) hasta 3772K (3½-ED) en A: y en B:.",13,10
DB " - Soporte INT 13h para que los programas comerciales puedan formatear en 2M.",13,10
DB " - Soportados comandos DISKCOPY y ¡SYS! hacia discos destino con formato 2M.",13,10,10
DB " ■ Con /ML se fuerza la instalación en memoria convencional (2MX se autoinstala",13,10
DB " automáticamente en memoria superior) y con /U se desinstala, si es posible.",13,10
DB 255
DB 13,10,10
DB " 2MX 2.1 - DISKETTE DRIVE CONTROLLER FOR STV DISKETTE FORMAT TECHNOLOGY.",13,10
DB " (c) 1994 Ciriaco García de Celis - Grupo Universitario de Informática.",13,10
DB " C/Renedo, 2, 4-C; 47005 Valladolid (Spain) - ciri@gui.uva.es - 2:341/21.8",13,10,10
DB " Syntax: 2MX [/ML] [/U]",13,10,10
DB " ■ 2MX is a CARDWARE utility; distribution is only authorized if no charge and",13,10
DB " with no modify. Before being executed without any option, the program becomes",13,10
DB " resident on memory, in order to handle 2M diskettes located in floppy drives.",13,10,10
DB " ■ 2MX requires:",13,10
DB " - A PC/XT computer with a modern BIOS (or with 2M-XBIOS installed instead).",13,10
DB " - At least one high-density floppy drive (double ones are not drived).",13,10
DB " - 5296 bytes of upper memory or 5392 bytes of conventional memory instead.",13,10
DB " - DOS and/or WINDOWS 3.X system, another environs are not still supported.",13,10
DB " - 2MF utility program to format 2M diskettes.",13,10
DB " ■ 2MX gives you:",13,10
DB " - Support for diskettes from 902K (5¼-DD) up to 3772K (3½-ED) on A: and B:.",13,10
DB " - INT 13h code improves a new 2M format service for high-level applications.",13,10
DB " - Supported DISKCOPY and ... SYS! commands into 2M diskette already formatted.",13,10,10
DB " ■ With /ML a conventional memory installation can be forced (this is an upper",13,10
DB " memory self-installating utility) and /U tells 2MX to uninstall, if possible.",13,10
DB 0
buffer_aux DB 64 DUP (0) ; buffer para alguna función del DOS
_PRINCIPAL ENDS
END inicio